[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 5393 - (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 :     ######################################################################
69 :    
70 :     =cut
71 :    
72 :     package FormulaUpToConstant;
73 :     @ISA = ('Value::Formula');
74 :    
75 :     sub Init {
76 :     main::PG_restricted_eval('sub FormulaUpToConstant {FormulaUpToConstant->new(@_)}');
77 :     }
78 :    
79 :     #
80 :     # Create an instance of a FormulaUpToConstant. If no constant
81 :     # is supplied, we add C ourselves.
82 :     #
83 :     sub new {
84 :     my $self = shift; my $class = ref($self) || $self;
85 :     #
86 :     # Copy the context (so we can modify it) and
87 :     # replace the usual Variable object with our own.
88 :     #
89 :     my $context = (Value::isContext($_[0]) ? shift : $self->context)->copy;
90 :     $context->{parser}{Variable} = 'FormulaUpToConstant::Variable';
91 :     #
92 :     # Create a formula from the user's input.
93 :     #
94 :     my $f = main::Formula($context,@_);
95 :     #
96 :     # If it doesn't have a constant already, add one.
97 :     # (should check that C isn't already in use, and look
98 :     # up the first free name, but we'll cross our fingers
99 :     # for now. Could look through the defined variables
100 :     # to see if there is already an arbitraryConstant
101 :     # and use that.)
102 :     #
103 :     unless ($f->{constant}) {$f = $f + "C", $f->{constant} = "C"}
104 :     #
105 :     # Check that the formula is linear in C.
106 :     #
107 :     my $n = $f->D($f->{constant});
108 :     Value->Error("Your formula isn't linear in the arbitrary constant '%s'",$f->{constant})
109 :     unless $n->isConstant;
110 :     #
111 :     # Make a version with an adaptive parameter for use in the
112 :     # comparison later on. We could like n0*C, but already have $n
113 :     # copies of C, so remove them. That way, n0 will be 0 when there
114 :     # are no C's in the student answer during the adaptive comparison.
115 :     # (Again, should really check that n0 is not in use already)
116 :     #
117 :     my $n0 = $context->variables->get("n0");
118 :     $context->variables->add(n0=>'Parameter') unless $n0 and $n0->{parameter};
119 :     $f->{adapt} = $f + "(n0-$n)$f->{constant}";
120 :     return bless $f, $class;
121 :     }
122 :    
123 :     ##################################################
124 :     #
125 :     # Remember that compare implements the overloaded perl <=> operator,
126 :     # and $a <=> $b is -1 when $a < $b, 0 when $a == $b and 1 when $a > $b.
127 :     # In our case, we only care about equality, so we will return 0 when
128 :     # equal and other numbers to indicate the reason they are not equal
129 :     # (this can be used by the answer checker to print helpful messages)
130 :     #
131 :     sub compare {
132 :     my ($l,$r) = @_; my $self = $l; my $context = $self->context;
133 :     $r = Value::makeValue($r,context=>$context);
134 :     #
135 :     # Not equal if the student value is constant or has no + C
136 :     #
137 :     return 2 if !Value::isFormula($r);
138 :     return 3 if !defined($r->{constant});
139 :     #
140 :     # If constants aren't the same, substitute the professor's in the student answer.
141 :     #
142 :     $r = $r->substitute($r->{constant}=>$l->{constant}) unless $r->{constant} eq $l->{constant};
143 :     #
144 :     # Compare with adaptive parameters to see if $l + n0 C = $r for some n0.
145 :     #
146 :     return -1 unless $l->{adapt} == $r;
147 :     #
148 :     # Check that n0 is non-zero (i.e., there is a multiple of C in the student answer)
149 :     # (remember: return value of 0 is equal, and non-zero is unequal)
150 :     #
151 :     return abs($context->variables->get("n0")->{value}) < $context->flag("zeroLevelTol");
152 :     }
153 :    
154 :     #
155 :     # Show hints by default
156 :     #
157 :     sub cmp_defaults {((shift)->SUPER::cmp_defaults,showHints => 1)};
158 :    
159 :     #
160 :     # Add useful messages, if the author requested them
161 :     #
162 :     sub cmp_postprocess {
163 :     my $self = shift; my $ans = shift;
164 :     $self->SUPER::cmp_postprocess($ans);
165 :     return unless $ans->{score} == 0 && !$ans->{isPreview};
166 :     return if $ans->{ans_message} || !$self->getFlag("showHints");
167 :     my $result = $ans->{correct_value} <=> $ans->{student_value}; # compare encodes the reason in the result
168 :     $self->cmp_Error($ans,"Note: there is always more than one posibility") if $result == 2 || $result == 3;
169 :     $self->cmp_Error($ans,"Your answer is not the most general solution")
170 :     if $result == 1 || ($result == 3 && $self->removeConstant == $ans->{student_value});
171 :     }
172 :    
173 :     #
174 :     # Get the name of the constant
175 :     #
176 :     sub constant {(shift)->{constant}}
177 :    
178 :     #
179 :     # Remove the constant and return a Formula object
180 :     #
181 :     sub removeConstant {
182 :     my $self = shift;
183 :     main::Formula($self->substitute($self->{constant}=>0))->reduce;
184 :     }
185 :    
186 :     #
187 :     # Override the differentiation so that we always return
188 :     # a Formula, not a FormulaUpToConstant (we don't want to
189 :     # add the C in again).
190 :     #
191 :     sub D {
192 :     my $self = shift;
193 :     $self->removeConstant->D(@_);
194 :     }
195 :    
196 :     ######################################################################
197 :     #
198 :     # This class repalces the Parser::Variable class, and its job
199 :     # is to look for new constants that aren't in the context,
200 :     # and add them in. This allows students to use ANY constant
201 :     # they want, and a different one from the professor. We check
202 :     # that the student only used ONE arbitrary constant, however.
203 :     #
204 :     package FormulaUpToConstant::Variable;
205 :     our @ISA = ('Parser::Variable');
206 :    
207 :     sub new {
208 :     my $self = shift; my $class = ref($self) || $self;
209 :     my $equation = shift; my $variables = $equation->{context}{variables};
210 :     my ($name,$ref) = @_; my $def = $variables->{$name};
211 :     #
212 :     # If the variable is not already in the context, add it
213 :     # and mark it as an arbitrary constant (for later reference)
214 :     #
215 :     if (!defined($def) && length($name) eq 1) {
216 :     $equation->{context}->variables->add($name => 'Real');
217 :     $equation->{context}->variables->set($name => {arbitraryConstant => 1});
218 :     $def = $variables->{$name};
219 :     }
220 :     #
221 :     # If the variable is an arbitrary constant
222 :     # Error if we already have a constant and it's not this one.
223 :     # Save the constant so we can check with it later.
224 :     #
225 :     if ($def && $def->{arbitraryConstant}) {
226 :     $equation->Error(["Your formula shouldn't have two arbitrary constants"],$ref)
227 :     if $equation->{constant} and $name ne $equation->{constant};
228 :     $equation->{constant} = $name;
229 :     }
230 :     #
231 :     # Do the usual Variable stuff.
232 :     #
233 :     $self->SUPER::new($equation,$name,$ref);
234 :     }
235 :    
236 :    
237 :     1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9