[system] / trunk / pg / macros / answerComposition.pl Repository:
ViewVC logotype

Annotation of /trunk/pg/macros/answerComposition.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2732 - (view) (download) (as text)

1 : dpvc 2732 ######################################################################
2 :     #
3 :     # An answer checker that determines if two functions compose
4 :     # to form a given function. (For use in problems where you ask
5 :     # a student to break a given function into a composition of two
6 :     # simpler functions, neither of which is allowed to be the identity
7 :     # function.)
8 :     #
9 :    
10 :     sub _answerComposition_init {}; # don't reload this file
11 :    
12 :     ######################################################################
13 :     #
14 :     # An answer checked to see if f composed with g matches a given function.
15 :     #
16 :     # Usage: COMPOSITION_ANS(f,g,options)
17 :     #
18 :     # where f and g are one possible decomposition of the target function
19 :     # (these are used to display the "correct" answer, and the composition
20 :     # is computed from them) and options are any of the options allowed
21 :     # by composition_ans_list() below.
22 :     #
23 :     # This function actually supplies TWO answer checkers, for the two
24 :     # previous answer blanks. So be sure to call it immediately after
25 :     # the answer blanks have been supplied. (It may be best to use the
26 :     # NAMED_COMPOSITION_ANS checker below, which specifies the answer
27 :     # blanks explicitly.)
28 :     #
29 :     # Example:
30 :     #
31 :     # BEGIN_TEXT
32 :     # \(f\circ g = (1+x)^2\) when
33 :     # \(f(x)\) = \{ans_rule(20)\} and \(g(x)\) = \{ans_rule(20)\}
34 :     # END_TEXT
35 :     # COMPOSITION_ANS("x^2","1+x");
36 :     #
37 :     #
38 :     sub COMPOSITION_ANS {
39 :     my $f = shift; my $g = shift;
40 :     my $fID = ANS_NUM_TO_NAME($main::ans_rule_count-1);
41 :     my $gID = ANS_NUM_TO_NAME($main::ans_rule_count);
42 :     my %ans = composition_ans_list($fID=>$f,$gID=>$g,@_);
43 :     ANS($ans{$fID},$ans{$gID});
44 :     }
45 :    
46 :    
47 :     ######################################################################
48 :     #
49 :     # An answer checked to see if f composed with g matches a given function.
50 :     #
51 :     # Usage: NAMED_COMPOSITION_ANS(fID=>f,gID->g,options)
52 :     #
53 :     # where fID and gID are the names of the answer rules for the functions
54 :     # f and g, and f and g are the answers for the functions. Options are
55 :     # any of the options allowed by composition_ans_list() below.
56 :     #
57 :     # This routine allows you to put the f and g answer blanks at any
58 :     # location in the problem, and in any order.
59 :     #
60 :     # Example:
61 :     #
62 :     # BEGIN_TEXT
63 :     # \(g\circ f = (1+x)^2\) when
64 :     # \(f(x)\) = \{NAMED_ANS('f',20)\} and \(g(x)\) = \{NAMED_ANS('g',20)\}
65 :     # END_TEXT
66 :     # NAMED_COMPOSITION_ANS(f => "x^2", g => "1+x");
67 :     #
68 :     sub NAMED_COMPOSITION_ANS {NAMED_ANS(composition_ans_list(@_))}
69 :    
70 :     ######################################################################
71 :     #
72 :     # This is an internal routine that returns the named answer checkers
73 :     # used by COMPOSITION_ANS and NAMED_COMPOSITION_ANS above.
74 :     #
75 :     # Usage: composition_ans_list(fID=>f,gID=>g,options)
76 :     #
77 :     # where fID and gID are the names of the answer rules for the functions
78 :     # and f and g are the answers for these functions. Options are from
79 :     # among:
80 :     #
81 :     # var => 'x' the name of the variable to use when
82 :     # both functions use the same one
83 :     #
84 :     # vars => ['x','t'] the names of the variabless for f and g
85 :     #
86 :     # showVariableHints => 1 or 0
87 :     # do/don't show errors when the variable
88 :     # used by the student is incorrect
89 :     #
90 :    
91 :     sub composition_ans_list {
92 :     my ($fID,$f,$gID,$g,%params) = @_; my @IDs = ($fID,$gID);
93 :     #
94 :     # Get options
95 :     #
96 :     $params{vars} = [$params{var},$params{var}] if $params{var} && !$params{vars};
97 :     $params{showVariableHints} = 1 unless defined($params{showVariableHints});
98 :     my $isPreview = $main::inputs_ref->{previewAnswers};
99 :     my $vars = $params{vars} || [];
100 :     my @options = (ignoreInfinity=>0,ignoreStrings=>0);
101 :     my ($i,$error);
102 :    
103 :     #
104 :     # Get correct answer data and determine which variables to use
105 :     #
106 :     $f = Value::Formula->new($f); $g = Value::Formula->new($g);
107 :     my %correct = ($fID => $f, $gID => $g);
108 :     my %x = ($fID => $vars->[0], $gID => $vars->[1]);
109 :     foreach $i (@IDs) {
110 :     unless ($x{$i}) {
111 :     die "Can't tell which variable to use for $correct{$i}: ".
112 :     "use var=>'x' or vars=>['x','y'] to specify it"
113 :     if scalar(keys %{$correct{$i}->{variables}}) > 1;
114 :     $x{$i} = (keys %{$correct{$i}->{variables}})[0];
115 :     }
116 :     die "$correct{$i} is not a function of $x{$i}"
117 :     unless defined($correct{$i}->{variables}{$x{$i}});
118 :     }
119 :     my %y = ($fID => $x{$gID}, $gID => $x{$fID});
120 :     my %ans = ($fID => message_cmp($f), $gID => message_cmp($g));
121 :     my $fog = $f->substitute($x{$fID}=>$g); # the composition
122 :    
123 :     #
124 :     # Check that the student formulas parse OK,
125 :     # produce a number, contain the correct variable,
126 :     # don't contain the composition itself in a simple way,
127 :     # and aren't the identity.
128 :     #
129 :     my %student = ($fID => $main::inputs_ref->{$fID},
130 :     $gID => $main::inputs_ref->{$gID});
131 :     foreach $i (@IDs) {
132 :     next unless defined($student{$i});
133 :     $student{$i} = Parser::Formula($student{$i});
134 :     if (!defined($student{$i})) {$error = 1; next}
135 :     $ans{$i}->{rh_ans}{preview_latex_string} = $student{$i}->TeX;
136 :     if ($student{$i}->type ne 'Number') {
137 :     $ans{$i} = $correct{$i}->cmp(@options);
138 :     $error = 1; next;
139 :     }
140 :     if ($x{$fID} ne $x{$gID} && defined($student{$i}->{variables}{$y{$i}})) {
141 :     $ans{$i}->{rh_ans}{ans_message} = "Your formula may not contain $y{$i}"
142 :     unless $isPreview || !$params{showVariableHints};
143 :     $error = 1; next;
144 :     }
145 :     if (!defined($student{$i}->{variables}{$x{$i}})) {
146 :     $ans{$i}->{rh_ans}{ans_message} = "Your formula is not a function of $x{$i}"
147 :     unless $isPreview || !$params{showVariableHints};
148 :     $error = 1; next;
149 :     }
150 :     if (($student{$i}->{tree}->class eq 'BOP' &&
151 :     ($fog == $student{$i}->{tree}{lop} || $fog == $student{$i}->{tree}{rop})) ||
152 :     ($student{$i}->{tree}->class eq 'UOP' && $fog == $student{$i}->{tree}{op})) {
153 :     $ans{$i}->{rh_ans}{ans_message} =
154 :     "Your formula may not have the composition as one of its terms";
155 :     $error = 1; next;
156 :     }
157 :     if ($fog == $student{$i}) {
158 :     $ans{$i}->{rh_ans}{ans_message} =
159 :     "Your formula my not be the composition itself";
160 :     $error = 1; next;
161 :     }
162 :     if ($student{$i} == $x{$i}) {
163 :     $ans{$i}->{rh_ans}{ans_message} = "The identity function is not allowed"
164 :     unless $isPreview;
165 :     $error = 1; next;
166 :     }
167 :    
168 :     }
169 :    
170 :     #
171 :     # If no error, nd both answers are given, check if compositions are equal
172 :     #
173 :     if (!$error && defined($student{$fID}) && defined($student{$gID})) {
174 :     if ($fog == $student{$fID}->substitute($x{$fID}=>$student{$gID})) {
175 :     $ans{$fID}->{rh_ans}{score} = $ans{$gID}->{rh_ans}{score} = 1;
176 :     }
177 :     }
178 :    
179 :     return (%ans);
180 :     }
181 :    
182 :    
183 :     ######################################################################
184 :     #
185 :     # Evaluator that always returns incorrect, with a given error
186 :     # message. Used by COMPOSITION_ANS to produce "dummy" answer
187 :     # checkers for the two parts of the composition.
188 :     #
189 :     sub message_cmp {
190 :     my $correct = shift;
191 :     my $answerEvaluator = new AnswerEvaluator;
192 :     $answerEvaluator->ans_hash(
193 :     type => "message",
194 :     correct_ans => $correct->string,
195 :     ans_message => $message,
196 :     );
197 :     $answerEvaluator->install_evaluator(sub {shift});
198 :     return $answerEvaluator;
199 :     }
200 :    
201 :     1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9