I've written some simple code that illustrates what has to be done:
Here's the source
##DESCRIPTION ## A very simple first problem ##ENDDESCRIPTION
##KEYWORDS('algebra')
DOCUMENT(); # This should be the first executable line in the problem.
loadMacros( "PG.pl", "PGbasicmacros.pl", "PGchoicemacros.pl", "PGanswermacros.pl", "PGauxiliaryFunctions.pl" );
TEXT(beginproblem()); $showPartialCorrectAnswers = 1;
$a = random(-10,-1,1); $b = random(1,11,1); $c = random(1,11,1); $d = random(1,11,1);
BEGIN_TEXT This problem demonstrates how you enter numerical answers into WeBWorK. $PAR Evaluate the expression \(3($a )($b -$c -2($d ))\): \{ ans_rule(10) \} $BR
END_TEXT
$ans = 3*($a)*($b-$c-2*($d));
# Here are the important lines where we build a new # answer evaluator using the old. # The new answer evaluator doesn't do much. Just adds # the message 'experimental'.
$old_ans_eval = num_cmp($ans);
# notice that num_cmp() is a function which PRODUCES an answer evaluator # or technically a pointer to the answer evaluator object. I prefer to think # of $old_ans_eval as actually containing the answer evaluator object.
$new_eval = new AnswerEvaluator;
# now we have a new, empty answer evaluator
$new_eval->install_evaluator(sub {my $rh_ans=shift; <br /> $rh_ans = $old_ans_eval->evaluate($rh_ans->{student_ans}); $rh_ans->{ans_message}='experimental'; $rh_ans;} );
# We have added a new filter to the new answer evaluator. # The structure of a filter is that it is supposed to be a subroutine # which accepts an answerHash object and returns an answerHash object. # The first problem is that an AnswerEvaluator is not subroutine # (it's more complicated) so one can't use # &$old_ans_eval($answer_hash) to process an answer hash, you need to say # $old_ans_eval->evaluate($answer_hash). # For this routine we wrap a subroutine around the old answer evaluator # so that it will behave correctly. # [Note: The evaluate routine in AnswerEvaluator should (IMHO) be modified # so it can accept filters which are answer evaluators as well as ones # which are ordinary subroutines. This won't be too hard, only a few hours work. ]
# Secondly, even that might not work -- most answer evaluators, the ones built # with all of the AnswerEvaluator tools accept either # a string input, OR an AnswerHash input. Some of the older answer evalatuors, # may not do this reliably # -- there has not yet been a lot of reuse of answer evaluators so # there has not been much testing in this area. # That appears to be the case with num_cmp type evaluators, so # instead of feeding it an answerHash # we find the student answer inside the AnswerHash and feed it that. It will # take some work, but # eventually we should be able to refit all answer evaluators # so that they will accept either a # string or an AnswerHash and can therefore be used one inside the other. # At the moment the evaluators produced by str_cmp aren't # even AnswerEvaluator objects, they are subroutines!
# The final thing the filter does is to add the string 'experimental' to the # answer message of the AnswerHash, just to prove that we've actually done # something. You could do much more.
# Final note -- I often use $rh_ans, the $rh_ means it's a pointer (reference) to a # hash value, this way I can remember that the scalar variable holds something # more complicated than a single value. I also use $r_ for reference, and $ra for # a reference to an array. Unfortunately I'm not consistent about this, but I use # technique when I'm likely to get confused as to what is a value and what is a pointer.
ANS($new_eval);
ENDDOCUMENT(); # This should be the last executable line in the problem.
Here is what the problem looks like. (1 pt) rochesterLibrary/setSampleAnswers/sample_compound_ans_eval.pg
This problem demonstrates how you enter numerical answers into WeBWorK.
Evaluate the expression :
|
WARNINGS µ¦å{h<p>
Reference <a href="http://webhost.math.rochester.edu/webworkdocs/docs/pglanguage/pod/AnswerHash">AnswerHash.pm</a>
</p>
<p><a href="http://webhost.math.rochester.edu/webworkdocs/discuss/msgReader$1741?mode=day"><| Post or View Comments |></a></p>
</td>
</tr>
</tbody></table>
</td>
</tr>
</tbody></table><br /><table width="100%" cellspacing="0" cellpadding="0" border="0" bgcolor="#666666">
<tbody><tr>
<td>
<table width="100%" cellspacing="1" cellpadding="5" border="0">
<tbody><tr>
<td bgcolor="#F5F5DC"><img width="16" height="16" border="0" alt="user" src="http://webhost.math.rochester.edu/mainResponderResources/icons/user" /> <a href="http://webhost.math.rochester.edu/webworkdocs/profiles/$45">Gavin LaRose</a> - <b>Re: using existing answer evaluators as filters</b> <a> </a><a title="Permanent link to this message in archives." href="http://webhost.math.rochester.edu/webworkdocs/discuss/msgReader$1742?y=2006&m=11&d=30"><img width="11" height="9" border="0" alt="blueArrow" src="http://webhost.math.rochester.edu/mainResponderResources/icons/leftArrow" /></a><br />
<font size="-2" class="dgsmall">8/28/2003; 8:38:52 AM (reads: 1387, responses:
0)</font> </td>
</tr>
<tr bgcolor="#ffffff">
<td>Hi Mike,
<p>
Thanks, as usual, for your rapid and extremely useful help. The essence
of your solution was my second attempt to get this to work, but it
didn't quite get to where it needed to before I decided that I couldn't
afford more time to play with it (I lost way more time than I intended
on forgetting that backslashes in problem code are actually double
tildes...).
</p>
<p>The end result? What seems as if it ought to be a much simpler
answer evaluator than I've arrived at to evaluate answers involving
differentials. For example, in the problem "If the area of a rectangle
is A(x,y) = xy, find the differential of this function: dA = [ ]." The
correct answer is dA = y dx + x dy, and we want to evaluate the
differentials (dx, dy) as single variables. To do this, I wrote an
evaluator that first filters the dx, dy (actually, filters d[var] for
all variables [var] specified in the answer evaluator options) into new
variables P,R, etc. (I skipped Q because that's used for the function
evaluator up to a constant), then uses fun_cmp to evaluate the
resulting function, and then filters P,R, etc. back to the original
variables. I'm pasting it in below. Comments for making it more
streamlined or elegant are welcomed.
</p>
<pre>sub diffl_fun_cmp {<br /> my $correctAns = shift();<br /> my %opts = @_;</pre><p><br /><br /> my @cAnsList = <br /> ((ref($correctAns) eq 'ARRAY')? @{$correctAns}:($correctAns));
# we rely on $opts{'var'} being defined as an array reference if ( defined( $opts{'var'} ) ) {<br /> $opts{'var'} = ( ref($opts{'var'}) ? $opts{'var'} : [$opts{'var'}] ); } else {<br /> $opts{'var'} = [ 'x' ]; }
my @outEvaluators = (); foreach my $cAns ( @cAnsList ) {<br /> push(@outEvaluators, diffl_eval($cAns, %opts));<br /> } return (wantarray) ? @outEvaluators : $outEvaluators[0]; } sub diffl_eval { <br /> my $cAns = shift();<br /> my %opts = @_;</p><p><br /><br /> my $evaluator = new AnswerEvaluator('correct_ans' => $cAns,<br /> 'type' => 'diffl_fun_cmp',<br /> 'original_correct_ans' =><br /> $cAns);<br /> $evaluator->install_pre_filter( ~~&replace_differentials_filter,<br /> %opts );<br /> $evaluator->install_evaluator( ~~&diffl_fun_eval, %opts );<br /> $evaluator->install_post_filter( ~~&restore_vars_filter, %opts );<br /> return $evaluator;<br />} sub diffl_fun_eval {<br /> my $rh_ans=shift; <br /> my %opts = @_;</p><p><br /><br /> my $cAns = $rh_ans->{'correct_ans'};
my @vars = ( @{$opts{'var'}}, @{$rh_ans->{'added_vars'}} ); $opts{'var'} = [ @vars ];
my $func_eval = fun_cmp($cAns, %opts); $rh_ans = $func_eval->evaluate( $rh_ans->{'student_ans'} );
return $rh_ans; } sub replace_differentials_filter {<br /> my $rh_ans = shift();<br /> my %opts = @_;</p><p><br /><br /> my $student_input = $rh_ans->input();<br /> my $correct_answer = $rh_ans->{'correct_ans'};
my @subs = ( 'P', 'R', 'S', 'M', 'N', 'O' ); my @addedVars = (); for ( my $i=0; $i<@{$opts{'var'}}; $i++ ) {<br /> $student_input =~ s/d$opts{'var'}->[$i]/$subs[$i]/g; $correct_answer =~ s/d$opts{'var'}->[$i]/$subs[$i]/g; push( @addedVars, $subs[$i] ); } $rh_ans->input($student_input); $rh_ans->{'correct_ans'} = $correct_answer; $rh_ans->{'added_vars'} = [ @addedVars ];
return $rh_ans; } sub restore_vars_filter {<br /> my $rh_ans = shift();<br /> my %opts = @_;</p><p><br /><br /> my $student_input = $rh_ans->input();<br /> my $correct_answer = $rh_ans->{'correct_ans'}; my $original_input = $rh_ans->{'original_student_ans'}; my $text_preview = $rh_ans->{'preview_text_string'}; my $latex_preview = $rh_ans->{'preview_latex_string'};
my @subs = ( 'P', 'R', 'S', 'M', 'N', 'O' ); for (my $i=0; $i<@{$opts{'var'}}; $i++) {<br /> $student_input =~ s/$subs[$i]/d$opts{'var'}->[$i]/g; $correct_answer =~ s/$subs[$i]/d$opts{'var'}->[$i]/g; $original_input =~ s/$subs[$i]/d$opts{'var'}->[$i]/g; $text_preview =~ s/$subs[$i]/d$opts{'var'}->[$i]/g; $latex_preview =~ s/$subs[$i]/d$opts{'var'}->[$i]/g; }
$rh_ans->input($student_input); $rh_ans->{'correct_ans'} = $correct_answer; $rh_ans->{'original_student_ans'} = $original_input; $rh_ans->{'preview_text_string'} = $text_preview; $rh_ans->{'preview_latex_string'} = $latex_preview; delete( $rh_ans->{'added_vars'} );
return $rh_ans; }
Thanks again, Mike
Gavin
<| Post or View Comments |>
|