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