WeBWorK Problems

Custom checkers on lists: Why different for lists of one item?

Custom checkers on lists: Why different for lists of one item?

by Paul Seeburger -
Number of replies: 1
I've been struggling to understand how to correct lists (checking one item of the list at a time) and to figure out how list_checkers work.  My goal with each of these has been to check for reduced fractions and mark the problem incorrect with feedback that the answer is correct, but needs to be simplified, if any number in the answer is not reduced/simplified.

When my list of solutions has two solutions in it, my checkers seem to work perfectly, but when there is only one solution in the lists, they do not seem to be working as I would expect.

Similarly, it seems that they do not work as I would expect when the student has only entered a single item in the list when the correct answer has two (or more?) items.  The checker does not give any helpful feedback, just returning incorrect, and it also seems to be simplifying the student answer in that case in the preview.

I am still learning about these, and much of this code is my attempt to edit example code from PCC problems or directly from Alex Jordan.

Here is an example of a checker that I used that only seems to work with more than one assignment.  When there is just one, it does not seem to see the individual items as assignments, and it was not seeming to work properly.  It was not exactly a list but a solution stated in the form:  n = 2 or n = 5.  In this case the correct answer only had one assignment and was in the form:  n = 4.

Here is my code for the solution set:

Defining the correct answer:
Context()->flags->set(showMixedNumbers => 0);
Context()->variables->are(n=>'Real');
Context()->strings->add("No solution"=>{NONE});
parser::Assignment->Allow;
Context()->operators->redefine(',',using=>',',from=>'Numeric');
Context()->operators->redefine('or',using=>',',from=>'Numeric');
Context()->operators->set(
  ','=>{string=>' or ',TeX=>'\hbox{ or }'},
  'or'=>{string=>' or ',TeX=>'\hbox{ or }'}
);
Context()->lists->set(List => {separator => " or "});
Context()->flags->set(
  reduceConstants=>0, # no decimals
  reduceConstantFunctions=>0, # combine 4+5*2?
  formatStudentAnswer=>'parsed', # no decimals
);

$possols = Compute("n = $an1");


Correcting the answer:

\{ SECTION_ANS($possols->cmp(  entry_type => "a possible solution",
  checker => sub {
    my ($correct,$student,$ans,$nth,$value) = @_;
    if ($correct->type eq "Assignment") {
      my ($svar,$sfrac) = $student->value; # get the variable and fraction
      if(Value::classMatch($sfrac,'Fraction'))
      {
        return 0 unless $sfrac->isReduced;
      }
    }
    return $correct == $student;
  },
  extra => sub {
    my ($student,$ansHash,$nth,$value) = @_;
    if($student eq "No solution")
    {
         $student->context->setError("This equation does have some solutions- look back at your work","",undef,undef,$Value::CMP_WARNING)
         unless $ans->{isPreview};
         return;
    }
    if ($student->type ne "Assignment" && $ansHash->{student_formula}->type ne "Assignment") {
      $student->context->setError("Your $nth solution should be written n = $US$US$US","",undef,undef,$Value::CMP_WARNING)
         unless $ans->{isPreview};
      return;
    }
    my ($svar,$sfrac) = $student->value; # get the variable and fraction
    if (Value::classMatch($sfrac,'Fraction') && !$sfrac->isReduced) {
      $student->context->setError("Your $nth $value is not reduced","",undef,undef,$Value::CMP_WARNING)
         unless $ans->{isPreview};
      return;
    }
    return Value::Real->typeMatch($student);
  }
)), $possols->ans_rule(30) \}


Then when I used a list_checker instead, I was very surprised that I needed to return a score of 2.0 from the list checker to award 100% even though there was only one element in the list.  I had thought that I needed to return a score equal to the number of items in the list in order to award 100%.

Do I need to use a different checker for problems in which there is only one solution than I use when there are two or more solutions?  If so, this seems more complicated than it should be.

Here is that code:

Defining answer:
Context("LimitedFraction")->flags->set(reduceFractions => 0);
Context()->flags->set(showMixedNumbers => 0);
Context()->variables->are(n=>'Real');
Context()->strings->add("No solution"=>{NONE});
parser::Assignment->Allow;
Context()->operators->redefine(',',using=>',',from=>'Numeric');
Context()->operators->redefine('or',using=>',',from=>'Numeric');
Context()->operators->set(
  ','=>{string=>' or ',TeX=>'\hbox{ or }'},
  'or'=>{string=>' or ',TeX=>'\hbox{ or }'}
);
Context()->lists->set(List => {separator => " or "});
Context()->operators->set(
'/' => {class => 'bizarro::BOP::divide', isCommand => 1},
'//' => {class => 'bizarro::BOP::divide', isCommand => 1},
' /' => {class => 'bizarro::BOP::divide', isCommand => 1},
'/ ' => {class => 'bizarro::BOP::divide', isCommand => 1},
);
Context()->flags->set(
  reduceConstants=>0, # no decimals
  reduceConstantFunctions=>0, # combine 4+5*2?
  formatStudentAnswer=>'parsed', # no decimals
);

$possols = Compute("n = $an1");



Correcting answer:
\{ SECTION_ANS($possols->cmp(  entry_type => "a possible solution",
  list_checker => sub {
my ($correct,$student,$ansHash,$value) = @_;
     return 0 if $ansHash->{isPreview};
     $student = $ansHash->{student_formula};
     $correct = $ansHash->{correct_ans};
     $student = Formula("$student"); $correct = Formula("$correct");
     return 0 unless ($correct == $student);
     Context()->flags->set(bizarroDiv=>1);
     delete $correct->{test_values}, $student->{test_values};
     my $OK = (($correct == $student) or ($student == $correct)) ;
     Context()->flags->set(bizarroDiv=>0);
     Value::Error("Your answer is correct, but please simplify it further.") unless $OK;
     return $OK*2;
}
)), $possols->ans_rule(30) \}


Thanks!

Paul
In reply to Paul Seeburger

Re: Custom checkers on lists: Why different for lists of one item?

by Alex Jordan -
Chiming in from a conference. I haven't had a chance to review your code, but I encountered the same (or a similar) issue in coding some problems a while back. I believe that if the answer is like List(Fraction(1,2)), then the list checker at some level incorrectly treats the answer like it was a list of two objects. I guess this is because Fraction(1,2)->value is itself a list of two objects.

To my recollection, this didn't really cause trouble with the part of my custom answer checking that did the checking, but it caused trouble with the part that did the scoring. In the above, the checker found 1 correct answer, but divided by 2 to give the student's score. This was specific to a list of one object; List(Fraction(1,2),Fraction(3,4)) behaved well.

So my hack in the custom checker was just to check if there was only one object in the list, and if the result was correct, increase the correct count from 1 to 2, and let the division by 2 happen to get 100%. I should not have done it this way. I should have researched the actual issue and fixed it, because now if it is fixed, I will have to undo my hack. (Thankfully the hack was only applied in a macro file, not individual problems, so I'll be able to undo the hack easily enough when the time comes.)