[system] / trunk / pg / macros / answerComposition.pl Repository: Repository Listing bbplugincoursesdistsnplrochestersystemwww Tue Oct 2 20:48:05 2007 UTC (12 years, 4 months ago) by sh002i
File size: 6854 byte(s)
```improved formatting for docs -- these were in pod sections but were all
formatted as verbatim sections, and i moved them into normal paragraphs,
lists, etc. should make things more readable from the web.
```

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