WeBWorK Problems

Answer checker woes

Answer checker woes

by Zak Zarychta -
Number of replies: 5

Hi there,

I am trying to solve a problem related to an old post

https://webwork.maa.org/moodle/mod/forum/discuss.php?d=2538 

i'm trying to write an answer checker that will compare the correct and student entered values and their strings to yield a correct answer.

So given a correct value of 123.46, student entered values of 123.456 will be incorrect but also would be 123.46000 and 123.460. So the student value has to match the correct value and their strings should be identical. The rationale for the interested may be found here 

https://ecampusontario.pressbooks.pub/prehealthsciencesmath1/chapter/2-2-accuracy-precision-and-rounding-rules/  

According to  https://webwork.maa.org/wiki/Custom_Answer_Checkers

The answer hash $anwserHash holds both the correct values and the strings of those values, respectively $ansHash{'correct_ans'} and $ansHash{'original_student_ans'}

With my limited perl and meagre knowledge of the inner workings of WeBWorK and after looking round, I've tried the following to no avail. It does not even grade the correct answer as correct. Also, If I chomp the strings 123.460 is graded as correct which is not the behaviour i'm looking for. Any help will be greatly appreciated.

$val = 123.45678

$ans = sprintf("%.2f", $val); # = 123.46

ANS( Real($ans)->cmp( checker=>sub {

( $correct, $student, $ansHash ) = @_;

$student_str = $ansHash{'original_student_ans'}; #unparsed student entry string

$correct_str = $ansHash{'correct_ans'};          #correct value string 

return ((($correct == $student) && ($correct_str == $student_str))? 1 : 0)

) );




In reply to Zak Zarychta

Re: Answer checker woes

by Zak Zarychta -
Okay, so it appears my perl was at fault here. Specifically how referenced a perl hash and the equality operator for strings (who knew!?). The current answer checker  below has the desired behaviour.
  
	$val = 123.45678;
	$ans = sprintf("%.2f", $val);

	ANS( Real($ans)->cmp( checker=>sub {
	( $correct, $student, $ansHash ) = @_;
	return (($correct == $student) && 
			($ansHash->{original_student_ans} eq $ansHash->{correct_ans})? 1 : 0)

	# checks 1.46 == student value and
	# checks "1.46" eq unformatted student string
	# so 1.46 is graded correct but 1.460 is graded incorrect
	}
	) );
  
In reply to Zak Zarychta

Re: Answer checker woes

by Steven Fiedler -

There appears to be an edge case in the code in the 11/3 post, which may create a complication.  If the answer value contains a trailing zero, it gets lopped off when $ans is cast as a math object.  Below is an example of such an instance.

DOCUMENT();
loadMacros("PGstandard.pl", "PGML.pl");

    $val = 123.4012;
    $ans = sprintf("%.2f", $val);
    
    
    BEGIN_PGML
Enter the number [$ans]: [___]
END_PGML
    

    ANS( Real($ans)->cmp( checker=>sub {
    ( $correct, $student, $ansHash ) = @_;
    return (($correct == $student) &&
            ($ansHash->{original_student_ans} eq $ansHash->{correct_ans})? 1 : 0)

    
    }
    ) );

ENDDOCUMENT();

In reply to Steven Fiedler

Re: Answer checker woes

by Steven Fiedler -
One thought... A work-around for the edge case could be to cast your answer as a string before sending it into the custom answer checker. This resolves the issue, but if the name of the answer variable is changed, the problem author would have to remember to also change it two locations: the Contex line as well as in the ANS line.


DOCUMENT();
loadMacros("PGstandard.pl", "PGML.pl");

$val = 123.4012;
$ans = sprintf("%.2f", $val);

BEGIN_PGML
Enter the number [$ans]: [___]
END_PGML

Context()->strings->add($ans=>{});
ANS( Compute($ans)->cmp( checker=>sub {
( $correct, $student, $ansHash ) = @_;
return (($correct == $student) &&
($ansHash->{original_student_ans} eq $ansHash->{correct_ans})? 1 : 0)


}
) );

ENDDOCUMENT();
In reply to Steven Fiedler

Re: Answer checker woes

by Danny Glin -

It's not a good idea to add a number to the Context as a string.  With your code above if a student enters 123.4012 they get the message "Operands of '*' can't be words" because 123.40 is now a word (string), and it is trying to multiply the word 123.40 by 12.

There are a few things to watch out for here, and maybe others that I'm not thinking of at the moment:

  • As Steven pointed out, as soon as you run Compute() to convert a string to a MathObject it will truncate any trailing zeroes, so you won't be working with the number you want.  This will affect both the answer checker and the correct answer that is shown to students.
    For the answer checker you can avoid this by doing the string comparison against the original string and not the string extracted from the MathObject.
    For the correct answer you can manually set the correct_ans_latex_string to be the original string.
  • Your condition seems redundant.  If you are checking that the student's string is identical to the correct string, you shouldn't also need to check that they are numerically equivalent, so you should be able to eliminate the first half of the conjunction.  Also, I don't think that "? 1 : 0" is necessary at the end as those are the return values for true and false respectively.
  • Insert the usual warnings about the fragility of comparing strings here.  If a student puts in any spaces it will be marked wrong, etc.

Here's my suggestion for the code:

DOCUMENT();
loadMacros("PGstandard.pl", "PGML.pl");
$val = 123.4012;
$ansString = sprintf("%.2f", $val);
$ans = Compute($ansString);
$ans->{correct_ans_latex_string}=$ansString;

BEGIN_PGML
Enter the number [$ans]: [___]
END_PGML

ANS( $ans->cmp( checker=>sub {
( $correct, $student, $ansHash ) = @_;
return ($ansHash->{original_student_ans} eq $ansString)
}
) );

ENDDOCUMENT();

In reply to Danny Glin

Re: Answer checker woes

by Steven Fiedler -
Thank you Danny for catching that.

The below code contains a few small revisions to display the untruncated number in the text of the problem as well as in the values that the student will see in the "Entered" and "Answer Preview" columns.

DOCUMENT();
loadMacros("PGstandard.pl", "PGML.pl");
$val = 123.4012;
$ansString = sprintf("%.2f", $val);
$bleh=12;
$ans = Compute($ansString);
$ans->{correct_ans_latex_string}=$ansString;

BEGIN_PGML
Enter the number [$ansString]: [___]
END_PGML
ANS( $ans->cmp( checker=>sub {
( $correct, $student, $ansHash ) = @_;

$orig_sval=$ansHash->{original_student_ans} ;
$ansHash->{student_ans} = $orig_sval; #The "Entered" column
$ansHash->{preview_latex_string}=$orig_sval; #The "Preview" column

return ($ansHash->{original_student_ans} eq $ansString)
}
) );

ENDDOCUMENT();