Parent Directory | Revision Log

Revision **5460** -
(**download**)
(**as text**)
(**annotate**)

*Wed Sep 5 23:56:19 2007 UTC*
(12 years, 4 months ago)
by *dpvc*

File size: 9624 byte(s)

File size: 9624 byte(s)

In making the checker work with +C, I forgot to add the parameter that provides for the student's answer to be off by a constant. (Since the C is actually a variable, not a parameter.) OOPS!

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

aubreyja at gmail dot com | ViewVC Help |

Powered by ViewVC 1.0.9 |