[system] / trunk / pg / macros / answerComposition.pl Repository: Repository Listing bbplugincoursesdistsnplrochestersystemwww

Mon Jun 11 18:16:40 2007 UTC (12 years, 7 months ago) by gage
File size: 7264 byte(s)
```Fixing docementation so that it can be read from the web.
```

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