Answer Evaluating Functions How to use them Numerical compare:
 ANS( strict_num_cmp( 3.14159 ) ); The student's answer must be a number in decimal or scientific notation which is
within .1 percent of 3.14159. This assumes $numRelPercentTolDefault has been set to .1.
 ANS(
strict_num_cmp( $answer,.01 ) ); The student's answer must be a number
within .01 percent of $answer (e.g. 3.14159 if $answer is 3.14159 or
$answer is "pi" or $answer is 4*atan(1)).
 ANS( frac_num_cmp( $answer) ) or ANS( frac_num_cmp( $answer,.01 )); The student's answer can be a number or fraction, e.g. 2/3.
 ANS(
arith_num_cmp( $answer) ) or ANS( arith_num_cmp( $answer,.01 )); The
student's answer can be an arithmetic expression, e.g. (2+3)/72^.5 .
 ANS( std_num_cmp( $answer) ) or ANS( std_num_cmp(
$answer,.01 )); The student's answer can contain elementary functions,
e.g. sin(.3+pi/2)
 You can also specify additional parameters for
controling the format of printed answers and for how answers close to
zero are handled. Further, there are versions which use a fixed
absolute tolerance rather than a relative percent tolerance. Finally,
there are versions which will accept a list of answers. Also num_cmp( )
is an alias for std_num_cmp( ). See standard Answer Evaluating Functions.
(see also num_cmp) String compare:
All the string comparison operators ignore initial and trailing white space.
 ANS( std_str_cmp( "W. Mozart" ) ); Accepts "W.
Mozart", "W. MOZarT" and so forth. Case insensitive. All internal
spaces treated as single spaces.
 ANS( std_cs_str_cmp( "Mozart" ) ); Rejects "mozart". Same as std_str_cmp but case sensitive.
 ANS( strict_str_cmp( "W. Mozart" ) ); Accepts only the exact string.
 ANS( unordered_str_cmp( "ABC" ) ); Accepts "a c B", "CBA" and so forth. Unordered, case insensitive, spaces ignored.
 ANS( unordered_cs_str_cmp( "ABC" ) ); Rejects "abc". Same as unordered_str_cmp but case sensitive.
 ANS( ordered_str_cmp( "ABC" ) ); Accepts "a b C", "A B C" and so forth. Ordered, case insensitive, spaces ignored.
 ANS( ordered_cs_str_cmp( "ABC" ) ); Rejects "abc", accepts "A BC" and so forth. Same as ordered_str_cmp but case sensitive.
 There are versions which will accept a list of answers.
See standard Answer Evaluating Functions.
(see also str_cmp)
Function compare:
 ANS( function_cmp( "cos(x)" ) ); Accepts cos(x), sin(x+pi/2), sin(x)^2 + cos(x) + cos(x)^2 1, etc.
This assumes $functVarDefault has been set to "x".
 ANS( function_cmp( $answer, "t" ) ); Assuming $answer is "cos(t)", accepts cos(t), etc.
 ANS( function_cmp_up_to_constant( "cos(x)" ) ); Accepts any antiderivative of sin(x), e.g. cos(x) + 5.
 ANS( function_cmp_up_to_constant( "cos(z)", "z" ) ); Accepts any antiderivative of sin(z), e.g. sin(z+pi/2) + 5.
 Further, there are versions which use a fixed absolute
tolerance rather than a relative percent tolerance. You can also specify additional parameters. See standard Answer Evaluating Functions.
(see also fun_cmp for another syntax)
What are they?
They extend the ability of the instructor to check a student's answer. Instead of just providing a correct
answer, they provide a function which evaluates the student's answer to determine if it is correct.
In other words the instructor can control both the correct answer and a method for comparing it with the
student's answer. Of course for many questions you can use the standard methods listed above.
For details on the standard Answer Evaluating Functions see
standard Answer Evaluating Functions.
For details on writing your own Answer Evaluating Functions see
writing your own Answer Evaluating Functions.
strict_num_cmp($correctAnswer); OR
strict_num_cmp($correctAnswer,$relpercentTol); OR
strict_num_cmp($correctAnswer,$relpercentTol,$format); OR
strict_num_cmp($correctAnswer,$relpercentTol,$format,$zeroLevel); OR
strict_num_cmp($correctAnswer,$relpercentTol,$format,$zeroLevel,$zeroLevelTol);
If not explicitly given, the various parameters have defaults which are defined in the Global.pm
file or the individual course webworkCourse.ph file. Typically $relpercentTol defaults to .1,
$format defaults to "", $zeroLevel defaults to 1E14, and $zeroLevelTol defaults to 1E12.
The student's answer must be a number (in integer, decimal, or
scientific notation or e or pi) which is within $relpercentTol percent
of $correctAnswer (i.e., abs($studentAnswer  $correctAnswer) <=
abs(.01*$relpercentTol*$correctAnswer)). However, if the $correctAnswer
has absolute value less than or equal to $zeroLevel, then the student's
answer must be, in absolute terms, within $zeroLevelTol of
$correctAnswer (i.e. abs($studentAnswer  $correctAnswer) <=
$zeroLevelTol). One must do this to handle floating point answers equal
to or near zero. The $format is passed to the prfmt printing routine
defined in PGansweracros.pl and must be either a format suitable for
sprintf, e.g. "%1.10g" , or "".
frac_num_cmp($correctAnswer); OR
frac_num_cmp($correctAnswer,$relpercentTol); etc.
The possible parameters are the same as for strict_num_cmp and have the same defaults.
Similar to strict_num_cmp except that the student's answer can be a number or a fraction
e.g. 2.1, 2/3, or 2.5/3.1, e, or pi/2. Syntax and arithmetic errors
are reported to the student.
arith_num_cmp($correctAnswer); etc.
The possible parameters are the same as for strict_num_cmp and have the same defaults.
Similar to frac_num_cmp except that the student's answer can be an arithmetic expression containing
+, , *, /, ^ or *, (, ), e, and pi e.g. (2+3)/72^.5 . Syntax and arithmetic errors
are reported to the student.
std_num_cmp($correctAnswer); etc.
The possible parameters are the same as for strict_num_cmp and have the same defaults.
Similar to arith_num_cmp except that the student's answer can also contain elementary functions and
constants pi and e. For example sin(pi/2)+ ln(e) is a complicated way to write the number 2.
Allowed functions are listed in "acceptable math functions".
Syntax, arithmetic, and other errors are reported to the student. num_cmp is a short name for std_num_cmp.
strict_num_cmp_abs($correctAnswer,$absTol,$format);
frac_num_cmp_abs($correctAnswer,$absTol,$format);
arith_num_cmp_abs($correctAnswer,$absTol,$format);
std_num_cmp_abs($correctAnswer,$absTol,$format);
These are the same as the above except that the student's answer must be
within $absTol of $correctAnswer (i.e. abs($studentAnswer  $correctAnswer) <=
$absTol). If not explicitly given,
the parameters $absTol and $format have same defaults as in strict_num_cmp
strict_num_cmp_list($relpercentTol,$format,@answerList);
strict_num_cmp_abs_list($absTol,$format,@answerList);
frac_num_cmp_list($relpercentTol,$format,@answerList);
frac_num_cmp_abs_list($absTol,$format,@answerList);
arith_num_cmp_list($relpercentTol,$format,@answerList);
arith_num_cmp_abs_list($absTol,$format,@answerList);
std_num_cmp_list($relpercentTol,$format,@answerList);
std_num_cmp_abs_list($absTol,$format,@answerList);
These are versions which accept a list of correct answers. @answerList can be an array or
an actual list, e.g. strict_num_cmp_list(25,"",1,2.3,5,...). This is especially useful
for instructors writing their own macros. Note that all parameters must be passed to these routines.
There are no defaults. There is also a "low level"
NUM_CMP_LIST ($tol,$format,$mode,$tolType,$zeroLevel,$zeroLevelTol,@answerList).
Look at the file PGanswermacros.pl for details.
std_str_cmp(
$correctAnswer ); The student's answer must match the string
$correctAnswer. The match is case insensitive and multiple spaces are
equivalent to 1 space. std_cs_str_cmp( $correctAnswer ); Same as std_str_cmp but case sensitive.
strict_str_cmp( $correctAnswer ); The student's answer must match the string $correctAnswer exactly.
unordered_str_cmp( $correctAnswer ); Usually used to match a list of
unordered letters. Unordered, case insensitive, spaces ignored. unordered_cs_str_cmp( $correctAnswer ); Same as unordered_str_cmp but case sensitive.
ordered_str_cmp( $correctAnswer ); Usually used to match a list of ordered letters. Ordered, case insensitive, spaces ignored.
ordered_cs_str_cmp($correctAnswer ); Same as ordered_str_cmp but case sensitive.
std_str_cmp_list(@correctAnswerList);
std_cs_str_cmp_list(@correctAnswerList);
strict_str_cmp_list(@correctAnswerList);
unordered_str_cmp_list(@correctAnswerList);
unordered_cs_str_cmp_list(@correctAnswerList);
ordered_str_cmp_list(@correctAnswerList);
ordered_cs_str_cmp_list(@correctAnswerList);
These are versions which accept a list of correct answers. @correctAnswerList can be an array or
an actual list, e.g. std_str_cmp_list("red", "white","blue"). This is especially useful
for instructors writing their own macros. Look at the file PGanswermacros.pl for details.
function_cmp($correctFunction) OR
function_cmp($correctFunction,$var) OR
function_cmp($correctFunction,$var,$llimit,$ulimit) OR
function_cmp($correctFunction,$var,$llimit,$ulimit,$relpercentTol) OR
function_cmp($correctFunction,$var,$llimit,$ulimit,$relpercentTol,$numOfPoints)
function_cmp($correctFunction,$var,$llimit,$ulimit,$relpercentTol,$numOfPoints,$zeroLevel)
function_cmp($correctFunction,$var,$llimit,$ulimit,$relpercentTol,$numOfPoints,$zeroLevel,$zeroLevelTol)
$correctFunction must be a string, e.g. "sin(x+pi/2)". $var is also a string, e.g. "x".
If not explicitly given, the various parameters have defaults which are defined in the Global.pm
file or the individual course webworkCourse.ph file. The $correctFunction must be defined on the
interval [$llimit,$ulimit).
Typically
$var defaults to "x", $llimit defaults to .00000001, $ulimit defaults to 1,
$relpercentTol defaults to .1, $numOfPoints defaults to 3, $zeroLevel defaults
to 1E14, and $zeroLevelTol defaults to 1E12. For example, if your function is sqrt(1x), you would
have to enter something like: function_cmp("sqrt(1x)","x",2,1)
The student's answer (a function) is evaluated at $numOfPoints random points
in the half open interval [$llimit,$ulimit). If the result is
within $relpercentTol*$correctFunction (with values of absolute value less than $zeroLevel handled
the same way as in std_num_cmp above) of $correctFunction evaluated at the same points,
the student's answer is correct. Allowed functions are listed
in "acceptable math functions".
Syntax, arithmetic, and other errors are reported to the student.
function_cmp_up_to_constant($correctFunction) OR
function_cmp_up_to_constant($correctFunction,$var) OR
function_cmp_up_to_constant($correctFunction,$var,$llimit,$ulimit) OR
function_cmp_up_to_constant($correctFunction,$var,$llimit,$ulimit,$relpercentTol) OR
function_cmp_up_to_constant($correctFunction,$var,$llimit,$ulimit,$relpercentTol,$numOfPoints) OR
function_cmp_up_to_constant($correctFunction,$var,$llimit,$ulimit,$relpercentTol,$numOfPoints,$maxConstantOfIntegration) OR
function_cmp_up_to_constant($correctFunction,$var,$llimit,$ulimit,$relpercentTol,$numOfPoints,$maxConstantOfIntegration,$zeroLevel)
OR
function_cmp_up_to_constant($correctFunction,$var,$llimit,$ulimit,$relpercentTol,$numOfPoints,$maxConstantOfIntegration,$zeroLevel,$zeroLevelTol)
$correctFunction must be a string, e.g. "sin(x) + .5*x^2". $var is also a string, e.g. "x".
If not explicitly given, the various parameters have defaults which are defined in the Global.pm
file or the individual course webworkCourse.ph file.
Typically $var defaults to "x", $llimit defaults to .00000001, $ulimit defaults to 1,
$relpercentTol defaults to .1, $numOfPoints defaults to 3, $maxConstantOfIntegration defaults to
1E8, $zeroLevel defaults
to 1E14, and $zeroLevelTol defaults to 1E12.
Similar to function_cmp, except the student's answer
only has to agree with the $correctFunction upto an additive constant.
This is designed to be used when asking questions on antiderivatives.
For technical reasons concerning floating point arithmetic, if the
additive constant, i.e. the constant of integration, is greater (in
absolute value) than $maxConstantOfIntegration AND is greater than
$maxConstantOfIntegration times the correct value, WeBWorK will given
an error message saying that it can not handle such a large constant of
integration. This is to prevent e.g. cos(x) + 1E20 or even 1E20 as
being accepted as a correct antiderivatives of sin(x) since floating
point arithmetic can not tell the difference between cos(x) + 1E20,
1E20, and cos(x) + 1E20.
function_cmp_abs($correctFunction,$var,$llimit,$ulimit,$absTol,$numOfPoints)
function_cmp_up_to_constant_abs($correctFunction,$var,$llimit,$ulimit,$absTol,$numOfPoints,,$maxConstantOfIntegration)
These are the same as the above except that the student's answer must be
within $absTol of $correctAnswer. If not explicitly given,
the various parameters have the same defaults as in function_cmp and function_cmp_up_to_constant.
Here is an example:
$testAnswer = sub { my $in = shift @_; # get the answer from the student my $correctQ; # variable will be set to 1 if the answer is # correct. my $PGanswerMessage ="" # message to be printed on screen if ($in eq "Mozart") { $correctQ =1; } else { $correctQ = 0; } ($correctQ, "Mozart" $in, $PGanswerMessage); }; ANS( $testAnswer );
If the student's answer is "Mozart", then it will be marked right.
If the "Show Answer" mode is set, then the correect answer "Mozart" will also appear,
regardless of the student's answer. The student's submitted answer will be printed and finally there will be no
PGanswerMessage since an empty message is returned.
Notice that we put $testAnswer into the ANS queue, not &testAnswer . This is because $testAnswer
contains a reference (or pointer) to the subroutine which evaluates the
answer. You can also think of the entire text of the subroutine
as residing in $testAnswer , ready to spring to life and evaluate the student's answer when called upon.
Of course if the student types "Wolfgang Amadeus Mozart"
their answer will be marked incorrect. However we can modify the answer
evaluation subroutine above
to handle this case, using Perl's pattern matching routines. $testAnswer = sub { my $in = @_; # get the answer from the student my $correctQ; # variable will be set to 1 if the answer is # correct. my $PGanswerMessage ="" # message to be printed on screen if ($in =~ /Mozart/) { # if the string "Mozart" appears in the answer $correctQ =1; } else { $correctQ = 0; } ($correctQ, "Wolfgang Amadeus Mozart", $in, $PGanswerMessage); }; ANS($testAnswer);
Now the student's answer will be marked correct for either "Mozart" or
for "Amadeus Mozart" or for "Mozart, Wolfgang A.". When asked to show
the correct answer, however "Wolfgang Amadeus Mozart" will be printed.
The full power of perl (Program Extraction and Report Language) can be used
to massage the student's answers and compare them to the correct one. More examples of evaluation functions
Here are two of the standard comparison answer evaluation functions which can be found
in the PGanswermacros.pl file. Look at that file for other examples.
To compare numerical answers use
ANS( std_num_cmp(3.456234567) );
Here is the actual code.
my $numRelPercentTolDefault = getNumRelPercentTolDefault(); my $numZeroLevelDefault = getNumZeroLevelDefault(); my $numZeroLevelTolDefault = getNumZeroLevelTolDefault(); my $numFormatDefault = getNumFormatDefault();
sub std_num_cmp { # compare numbers allowing use of elementary functions my ($correctAnswer,$relpercentTol,$format,$zeroLevel,$zeroLevelTol) = @_;
$relpercentTol = $numRelPercentTolDefault unless defined $relpercentTol; my $tol = .01*$relpercentTol; $format = $numFormatDefault unless defined $format; $zeroLevel = $numZeroLevelDefault unless defined $zeroLevel; $zeroLevelTol = $numZeroLevelTolDefault unless defined $zeroLevelTol; NUM_CMP($correctAnswer,$tol,$format,'std','rel',$zeroLevel,$zeroLevelTol); }
sub NUM_CMP { # low level numeric compare my ($correctAnswer,$tol,$format,$mode,$tolType,$zeroLevel,$zeroLevelTol) = @_; my $formattedCorrectAnswer = prfmt($correctAnswer,$format ); my $answer_evaluator = sub { my $in = shift @_; my $PGanswerMessage = ''; my ($inVal,$correctVal); $inVal = ''; $correctAnswer = &math_constants($correctAnswer); my $formattedSubmittedAnswer = ''; $@=''; if ($correctAnswer =~ /S/) {$correctVal = eval($correctAnswer);} else { $@ = ' ';} if ($@ or not is_a_number($correctVal)) { ##error message from eval or above $formattedSubmittedAnswer = $@; $formattedSubmittedAnswer =clean_up_error_msg($formattedSubmittedAnswer); $PGanswerMessage = 'Tell your professor that there is an error in this problem'; return (0,$formattedCorrectAnswer,$formattedSubmittedAnswer,$PGanswerMessage); } $in = &math_constants($in);
MODE_CASE: { ## bare block for "case" statement if ($mode eq 'std') { last MODE_CASE; } if ($mode eq 'strict') { unless (is_a_number($in)) { $PGanswerMessage = 'You must enter a number, e.g. 6, 5.3, or 6.12E3'; $formattedSubmittedAnswer = 'Incorrect number format'; return (0,$formattedCorrectAnswer,$formattedSubmittedAnswer,$PGanswerMessage); } last MODE_CASE; } if ($mode eq 'arith') { unless (is_an_arithmetic_expression($in)) { $PGanswerMessage = 'You must enter an arithmetic expression, e.g. 6 or (2.3*4+5/3)^2'; $formattedSubmittedAnswer = 'Not an arithmetic expression'; return (0,$formattedCorrectAnswer,$formattedSubmittedAnswer,$PGanswerMessage); } last MODE_CASE; } if ($mode eq 'frac') { unless (is_a_fraction($in)) { $PGanswerMessage = 'You must enter a number or fraction , e.g. 6 or 7/13'; $formattedSubmittedAnswer = 'Not a number or fraction'; return (0,$formattedCorrectAnswer,$formattedSubmittedAnswer,$PGanswerMessage); } last MODE_CASE; } $PGanswerMessage = 'Tell your professor that there is an error in his or her answer mechanism'; $formattedSubmittedAnswer = $in; return (0,$formattedCorrectAnswer,$formattedSubmittedAnswer,$PGanswerMessage); } # end of MODE_CASES bare block
$@=''; if ($in =~ /S/) {$inVal = eval($in);} else { $@ = ' ';} if ($@) { ##error message from eval or above $formattedSubmittedAnswer = $@; $formattedSubmittedAnswer =clean_up_error_msg($formattedSubmittedAnswer); $PGanswerMessage = 'There is a syntax error in your answer'; return (0,$formattedCorrectAnswer,$formattedSubmittedAnswer,$PGanswerMessage); } else {$formattedSubmittedAnswer = prfmt($inVal,$format);}
unless ($tolType eq 'abs') { if ( abs($correctVal) <= $zeroLevel) {$tol = $zeroLevelTol;} ## want $tol to be non zero else {$tol = abs($tol*$correctVal);} } my $correctQ =0; my $is_a_number = is_a_number($inVal); $correctQ = 1 if (($is_a_number) and (abs( $inVal  $correctVal ) <= $tol)); if ($@) {$PGanswerMessage = 'There is a syntax error in your answer';} elsif (not $is_a_number){$PGanswerMessage = 'Your answer does not evaluate to a number';} ($correctQ,$formattedCorrectAnswer,$formattedSubmittedAnswer,$PGanswerMessage); }; $answer_evaluator; }
Now
ANS( std_num_cmp( 2 ) );
will mark as correct 2, 2*cos(0), 1+sin(pi/2), sqrt(4), 8^(1/3), etc.
The output of std_num_cmp is placed in the ANS queue. This output
is an answer evaluation function (or subroutine) which will mark 2 and
its variations correct.
NUM_CMP uses the following subroutines:
sub is_a_number { my ($num) = @_; $num =~ s/^s*//; ## remove initial spaces $num =~ s/s*$//; ## remove trailing spaces my $is_a_number = 0; ## the following is copied from the online perl manual if ($num =~ /^([+]?)(?=d.\d)d*(.\d*)?([Ee]([+]?d+))?$/){$is_a_number = 1;} $is_a_number; }
sub is_a_fraction {
sub is_an_arithmetic_expression {
sub math_constants { my($in) = @_; $in =~s/bpib/(4*atan2(1,1))/ge; $in =~s/beb/(exp(1))/ge; $in =~s/^/**/g; $in; }
sub clean_up_error_msg { my $msg = $_[0]; $msg =~ s/at.*line [d]*//g; $msg =~ s/called//g; $msg =~ s/&main:://g; $msg =~ s/chunk [d]*//g; $msg; }
sub prfmt { my($number,$format) = @_; # attention, the order of format and number are reversed my $out; if ($format) {$out = sprintf($format, $number);} else {$out = $number;} $out; }
To compare string answers we could use
ANS( std_str_cmp( "Mozart" ) );
This will mark as correct "Mozart", "MOZART", "MOZART " and so forth. sub std_cs_str_cmp { # compare strings case sensitive my ($correctAnswer) = shift @_; # get the correct answer my $normalizedCorrectAnswer = $correctAnswer; $normalizedCorrectAnswer=~ s/s*$//; # remove trailing whitespace $normalizedCorrectAnswer=~ s/^s*//; # remove initial spaces $normalizedCorrectAnswer=~ s/s+/ /g; # replace spaces by single space
my $answer_evaluator = sub { my $in = shift @_; $in=~ s/s*$//; # remove trailing whitespace $in=~ s/^s*//; # remove initial spaces $in=~ s/s+/ /g; # replace spaces by single space my $correctQ = ($in eq $normalizedCorrectAnswer) ? 1: 0; ($correctQ,$normalizedCorrectAnswer,$in,""); }; $answer_evaluator; }
Now
ANS( std_str_cmp( "Mozart" ) );
will mark as correct "Mozart", "MOZART", "MOZART " and so forth.
The output of std_str_cmp is placed in the ANS queue. This output
is an answer evaluation function (or subroutine) which will mark "MOZART" and
its variations correct.
< Post or View Comments >
