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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

1 : dpvc 5393 loadMacros("MathObjects.pl");
2 :    
3 :     sub _parserFormulaUpToConstant_init {FormulaUpToConstant::Init()}
4 :    
5 :     =head1 FormulaUpToConstant();
6 :    
7 :     ######################################################################
8 :     #
9 :     # This file implements the FormulaUpToConstant object, which is
10 :     # a formula that is only unique up to a constant (i.e., this is
11 :     # an anti-derivative). Students must include the "+C" as part of
12 :     # their answers, but they can use any (single-letter) constant that
13 :     # they want, and it doesn't have to be the one the professor used.
14 :     #
15 :     # To use FormulaWithConstat objects, load this macro file at the
16 :     # top of your problem:
17 :     #
18 :     # loadMacros("parserFormulaUpToConstant");
19 :     #
20 :     # then create a formula with constant as follows:
21 :     #
22 :     # $f = FormulaUpToConstant("sin(x)+C");
23 :     #
24 :     # Note that the C should NOT already be a variable in the Context;
25 :     # the FormulaUpToConstant object will handle adding it in for
26 :     # you. If you don't include a constant in your formula (i.e., if
27 :     # all the variables that you used are already in your Context,
28 :     # then the FormulaUpToConstant object will add "+C" for you.
29 :     #
30 :     # The FormulaUpToConstant should work like any normal Formula,
31 :     # and in particular, you use $f->cmp to get its answer checker.
32 :     #
33 :     # ANS($f->cmp);
34 :     #
35 :     # Note that the FormulaUpToConstant object creates its only private
36 :     # copy of the current Context (so that it can add variables without
37 :     # affecting the rest of the problem). You should not notice this
38 :     # in general, but if you need to access that context, use $f->{context}.
39 :     # E.g.
40 :     #
41 :     # Context($f->{context});
42 :     #
43 :     # would make the current context the one being used by the
44 :     # FormulaUpToConstant, while
45 :     #
46 :     # $f->{context}->variables->names
47 :     #
48 :     # would return a list of the variables in the private context.
49 :     #
50 :     # To get the name of the constant in use in the formula,
51 :     # use
52 :     #
53 :     # $f->constant.
54 :     #
55 :     # If you combine a FormulaUpToConstant with other formulas,
56 :     # the result will be a new FormulaUpToConstant object, with
57 :     # a new Context, and potentially a new + C added to it. This
58 :     # is likely not what you want. Instead, you should convert
59 :     # back to a Formula first, then combine with other objects,
60 :     # then convert back to a FormulaUpToConstant, if necessary.
61 :     # To do this, use the removeConstant() method:
62 :     #
63 :     # $f = FormulaUpToConstant("sin(x)+C");
64 :     # $g = Formula("cos(x)");
65 :     # $h = $f->removeConstant + $g; # $h will be "sin(x)+cos(x)"
66 :     # $h = FormulaUpToConstant($h); # $h will be "sin(x)+cos(x)+C"
67 :     #
68 : dpvc 5394 # The answer evaluator by default will give "helpful" messages
69 :     # to the student when the "+ C" is left out. You can turn off
70 :     # these messages using the showHints option to the cmp() method:
71 :     #
72 :     # ANS($f->cmp(showHints => 0));
73 :     #
74 : dpvc 5393 ######################################################################
75 :    
76 :     =cut
77 :    
78 :     package FormulaUpToConstant;
79 :     @ISA = ('Value::Formula');
80 :    
81 :     sub Init {
82 :     main::PG_restricted_eval('sub FormulaUpToConstant {FormulaUpToConstant->new(@_)}');
83 :     }
84 :    
85 :     #
86 :     # Create an instance of a FormulaUpToConstant. If no constant
87 :     # is supplied, we add C ourselves.
88 :     #
89 :     sub new {
90 :     my $self = shift; my $class = ref($self) || $self;
91 :     #
92 :     # Copy the context (so we can modify it) and
93 :     # replace the usual Variable object with our own.
94 :     #
95 :     my $context = (Value::isContext($_[0]) ? shift : $self->context)->copy;
96 :     $context->{parser}{Variable} = 'FormulaUpToConstant::Variable';
97 :     #
98 :     # Create a formula from the user's input.
99 :     #
100 :     my $f = main::Formula($context,@_);
101 :     #
102 :     # If it doesn't have a constant already, add one.
103 :     # (should check that C isn't already in use, and look
104 :     # up the first free name, but we'll cross our fingers
105 :     # for now. Could look through the defined variables
106 :     # to see if there is already an arbitraryConstant
107 :     # and use that.)
108 :     #
109 :     unless ($f->{constant}) {$f = $f + "C", $f->{constant} = "C"}
110 :     #
111 :     # Check that the formula is linear in C.
112 :     #
113 :     my $n = $f->D($f->{constant});
114 :     Value->Error("Your formula isn't linear in the arbitrary constant '%s'",$f->{constant})
115 :     unless $n->isConstant;
116 :     #
117 :     # Make a version with an adaptive parameter for use in the
118 :     # comparison later on. We could like n0*C, but already have $n
119 :     # copies of C, so remove them. That way, n0 will be 0 when there
120 :     # are no C's in the student answer during the adaptive comparison.
121 :     # (Again, should really check that n0 is not in use already)
122 :     #
123 :     my $n0 = $context->variables->get("n0");
124 :     $context->variables->add(n0=>'Parameter') unless $n0 and $n0->{parameter};
125 :     $f->{adapt} = $f + "(n0-$n)$f->{constant}";
126 :     return bless $f, $class;
127 :     }
128 :    
129 :     ##################################################
130 :     #
131 :     # Remember that compare implements the overloaded perl <=> operator,
132 :     # and $a <=> $b is -1 when $a < $b, 0 when $a == $b and 1 when $a > $b.
133 :     # In our case, we only care about equality, so we will return 0 when
134 :     # equal and other numbers to indicate the reason they are not equal
135 :     # (this can be used by the answer checker to print helpful messages)
136 :     #
137 :     sub compare {
138 :     my ($l,$r) = @_; my $self = $l; my $context = $self->context;
139 :     $r = Value::makeValue($r,context=>$context);
140 :     #
141 :     # Not equal if the student value is constant or has no + C
142 :     #
143 :     return 2 if !Value::isFormula($r);
144 :     return 3 if !defined($r->{constant});
145 :     #
146 :     # If constants aren't the same, substitute the professor's in the student answer.
147 :     #
148 :     $r = $r->substitute($r->{constant}=>$l->{constant}) unless $r->{constant} eq $l->{constant};
149 :     #
150 :     # Compare with adaptive parameters to see if $l + n0 C = $r for some n0.
151 :     #
152 :     return -1 unless $l->{adapt} == $r;
153 :     #
154 :     # Check that n0 is non-zero (i.e., there is a multiple of C in the student answer)
155 :     # (remember: return value of 0 is equal, and non-zero is unequal)
156 :     #
157 :     return abs($context->variables->get("n0")->{value}) < $context->flag("zeroLevelTol");
158 :     }
159 :    
160 :     #
161 :     # Show hints by default
162 :     #
163 :     sub cmp_defaults {((shift)->SUPER::cmp_defaults,showHints => 1)};
164 :    
165 :     #
166 :     # Add useful messages, if the author requested them
167 :     #
168 :     sub cmp_postprocess {
169 :     my $self = shift; my $ans = shift;
170 :     $self->SUPER::cmp_postprocess($ans);
171 :     return unless $ans->{score} == 0 && !$ans->{isPreview};
172 :     return if $ans->{ans_message} || !$self->getFlag("showHints");
173 :     my $result = $ans->{correct_value} <=> $ans->{student_value}; # compare encodes the reason in the result
174 :     $self->cmp_Error($ans,"Note: there is always more than one posibility") if $result == 2 || $result == 3;
175 :     $self->cmp_Error($ans,"Your answer is not the most general solution")
176 :     if $result == 1 || ($result == 3 && $self->removeConstant == $ans->{student_value});
177 :     }
178 :    
179 :     #
180 :     # Get the name of the constant
181 :     #
182 :     sub constant {(shift)->{constant}}
183 :    
184 :     #
185 :     # Remove the constant and return a Formula object
186 :     #
187 :     sub removeConstant {
188 :     my $self = shift;
189 :     main::Formula($self->substitute($self->{constant}=>0))->reduce;
190 :     }
191 :    
192 :     #
193 :     # Override the differentiation so that we always return
194 :     # a Formula, not a FormulaUpToConstant (we don't want to
195 :     # add the C in again).
196 :     #
197 :     sub D {
198 :     my $self = shift;
199 :     $self->removeConstant->D(@_);
200 :     }
201 :    
202 :     ######################################################################
203 :     #
204 :     # This class repalces the Parser::Variable class, and its job
205 :     # is to look for new constants that aren't in the context,
206 :     # and add them in. This allows students to use ANY constant
207 :     # they want, and a different one from the professor. We check
208 :     # that the student only used ONE arbitrary constant, however.
209 :     #
210 :     package FormulaUpToConstant::Variable;
211 :     our @ISA = ('Parser::Variable');
212 :    
213 :     sub new {
214 :     my $self = shift; my $class = ref($self) || $self;
215 :     my $equation = shift; my $variables = $equation->{context}{variables};
216 :     my ($name,$ref) = @_; my $def = $variables->{$name};
217 :     #
218 :     # If the variable is not already in the context, add it
219 :     # and mark it as an arbitrary constant (for later reference)
220 :     #
221 :     if (!defined($def) && length($name) eq 1) {
222 :     $equation->{context}->variables->add($name => 'Real');
223 :     $equation->{context}->variables->set($name => {arbitraryConstant => 1});
224 :     $def = $variables->{$name};
225 :     }
226 :     #
227 :     # If the variable is an arbitrary constant
228 :     # Error if we already have a constant and it's not this one.
229 :     # Save the constant so we can check with it later.
230 :     #
231 :     if ($def && $def->{arbitraryConstant}) {
232 :     $equation->Error(["Your formula shouldn't have two arbitrary constants"],$ref)
233 :     if $equation->{constant} and $name ne $equation->{constant};
234 :     $equation->{constant} = $name;
235 :     }
236 :     #
237 :     # Do the usual Variable stuff.
238 :     #
239 :     $self->SUPER::new($equation,$name,$ref);
240 :     }
241 :    
242 :    
243 :     1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9