WeBWorK Problems

Reducing fractions in a list of assignments

Reducing fractions in a list of assignments

by Chris Hughes -
Number of replies: 3
I'm working on a problem that has this structure
x^2 = 1/49
And I'd like the students to answer with
x=1/7,x=-1/7 
Thanks to Davide's help (http://webwork.maa.org/moodle/mod/forum/discuss.php?d=3036) I have the customized answer helper to give hints to the students such as Are you sure you have all the solutions?

I'm having trouble requiring the students to reduce their fractions. I have read http://webwork.maa.org/moodle/mod/forum/discuss.php?d=3002 which seems like it will be relevant- I have put my attempt below, but I now get the message
Your first value isn't solution (it looks like a variable equal to a fraction)
Allowing the solution to be an assignment (x=....) is making this tricky- otherwise I could use the solution from http://webwork.maa.org/moodle/mod/forum/discuss.php?d=2879

Any help would be greatly appreciated.

 DOCUMENT();
loadMacros(
 "PGstandard.pl",
 "MathObjects.pl",
 "parserAssignment.pl",
 "answerHints.pl",
 "PGML.pl",
 "contextFraction.pl",
 );
##############################################
Context("LimitedFraction")->flags->set(reduceFractions => 0);
parser::Assignment->Allow;
Context()->operators->redefine(',',using=>',',from=>'Numeric');
Context()->operators->redefine('or',using=>',',from=>'Numeric');
Context()->operators->set(','=>{string=>' or ',TeX=>' or '}, 'or'=>{string=>' or ',TeX=>' or '});
$var = "x";
#$a = Fraction(1,random(2,10,1));
$a = Fraction(1,7);
#$ans = Compute("$var = -$a, $var = $a");
$ans = Compute("-$a, $a");
##############################################
TEXT(beginproblem());
BEGIN_PGML
Solve the quadratic equation

 [` [$var]^2 = [$a**2] `]
 [______________________]
END_PGML
##############################################
$showPartialCorrectAnswers = 1;
ANS($ans->cmp(entry_type => "solution",
 checker => sub {
 my ($correct,$student,$ans,$nth,$value) = @_;
 my ($cvar,$cfrac) = ($correct->{lop},$correct->{rop}); # get the variable and fraction
 my ($svar,$sfrac) = ($student->{lop},$student->{rop}); # get the variable and fraction
 return 0 unless Value::classMatch($sfrac,'Fraction');
 return $cfrac == $sfrac if $sfrac->isReduced;
 $correct->context->setError("Your $nth $value is not reduced","",undef,undef,$Value::CMP_WARNING)
 unless $ans->{isPreview};
 return 0;
 },
 extra => sub {
 my ($student,$ansHash,$nth,$value) = @_;
 if ($student->type ne "Assignment" && $ansHash->{student_formula}->type ne "Assignment") {
 $student->context->setError("$nth solution: $cfrac $student $check2->{ans_message} it should be written $var = ___","",undef,undef,$Value::CMP_WARNING);
 return;
 }
 return Value::Real->typeMatch($student);
 })->withPostFilter(
AnswerHints(
 ["$var=$a","$var=-$a"] => "Are you sure you have all the solutions?",
 [$a,-$a] => ["Your solution is a correct one, but you should write $var = ___<br>Are you sure you have all the solutions?",replaceMessage=>1],
 ["$a,-$a","-$a,$a"] => ["Your solutions are correct, but you should write $var = ___",replaceMessage=>1],
)));
ENDDOCUMENT(); 
In reply to Chris Hughes

Re: Reducing fractions in a list of assignments

by Davide Cervone -
There are a couple of issues behind this. First, your $ans is Compute("-$a, $a"), so you are looking for a list of numbers but have entered a list of assignments. That is the reason for the warning message. (Also, it should really be entry_type => "a solution" in order to get this error message to read correctly; that was my fault).

I see that you have commented out the

    $ans = Compute("$var = -$a, $var = $a");
that is the correct answer. Fixing that gets you past that message, but gets you pick screens due to an error in your checker.

The problem here is that the checker functions gets a MathObject that represents the assignment, not a parse tree for the formula that created the assignment. You are trying to take the left and right operands of the equality, but that is trying to use the object as a parse tree. The actual MathObject result of an equality is a List that contains the variable name as the first element and the value as the second element in the list (you do not normally have to deal with it at this level, so aren't aware of that). So to get the variable and fraction, use

    my ($var,$frac) = $student->value;
(If you go back to the discussion you cite, you will see that this is how it works there.)

Fixing that does allow the answer to be marked as correct, but it also produces a bunch of warnings about undefined values. That has to do with the fact that the assignment is really a list, and it appears that the lists are being broken up into the separate pieces during the checking; I haven't tracked that down completely, but know how to prevent it (see below). The parserAssignment object seems to be a bit buggy in a couple of places, and it might be worth reworking it.

For now, however, you can add

    return 0 unless $correct->type eq "Assignment";
to the checker function just after obtaining the value for $correct. This will avoid the warning messages for the case where the checker is being called at the wrong time (and should not hurt anything when I fix the bugs in the assignment object).

There are still a couple of other issues, however. First, in your extra routine, you use $cfrac and $check2, which are undefined. I'm not sure exactly what you are after, but if you remove them, that avoids the warning messages.

Another problem is that where you have the error message about reduced fractions isn't correct. The checker routine may be called multiple times on each student answer (as it is checked against the various correct answers to see if it matches one). So the error messages it produces are ignored (in case an error is produced when checking against a correct answer that it doesn't match). After all the correct answers are found the incorrect ones are passed to the extra routine for final error messages (for things like syntax messages). So you need the checker function to mark an unreduced fraction as incorrect (but no message given), and the extra function to produce the error message about not being reduced.

I've fixed those issues in the code the follows:

    loadMacros(
     "PGstandard.pl",
     "MathObjects.pl",
     "parserAssignment.pl",
     "answerHints.pl",
     "PGML.pl",
     "contextFraction.pl",
     );
    
    ##############################################
    
    Context("LimitedFraction")->flags->set(reduceFractions => 0);
    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 "});
   
    $var = "x";
    $a = Fraction(1,random(2,10,1));
    $ans = Compute("$var = -$a, $var = $a");
    
    ##############################################

    TEXT(beginproblem());
    BEGIN_PGML
    Solve the quadratic equation
    
    [` [$var]^2 = [$a**2] `]
    [______________________]
    END_PGML
    
    ##############################################

    $showPartialCorrectAnswers = 1;
    ANS($ans->cmp(
      entry_type => "a solution",
      checker => sub {
        my ($correct,$student,$ans,$nth,$value) = @_;
        if ($correct->type eq "Assignment") {
          my ($svar,$sfrac) = $student->value; # get the variable and fraction
          return 0 unless Value::classMatch($sfrac,'Fraction') && $sfrac->isReduced;
        }
        return $correct == $student;
      },
      extra => sub {
        my ($student,$ansHash,$nth,$value) = @_;
        if ($student->type ne "Assignment" && $ansHash->{student_formula}->type ne "Assignment") {
          $student->context->setError("$nth $value: $student should be written $var = ___","",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);
      }
    )->withPostFilter(AnswerHints(
      ["$var=$a","$var=-$a"] => "Are you sure you have all the solutions?",
      [$a,-$a] => ["Your solution is a correct one, but you should write $var = ___<br>Are you sure you have all the solutions?",replaceMessage=>1],
      ["$a,-$a","-$a,$a"] => ["Your solutions are correct, but you should write $var = ___",replaceMessage=>1],
    )));
I see that you have tried to make the comma display as an " or ", but this isn't working. The reason is that the result of parsing x = -1/7, x = 1/7 is actually a constant List (basically the list (("x",-1/7),("x",1/7))), and so the item whose string and TeX form is being used is that constant List, so it is the List string and TeX function that is called. These don't use the context's comma operator to do their formatting. You can set the List object's separator value (as I do above) to get it to stringify using " or ", but unfortunately the TeX output is hardcoded with a comma (that need to be fixed).

Finally, something seems to be not working with the AnswerHints, as they are not being applied when they should be. I have to look into that further, but wanted to get this much out before doing that.

Hope that helps.

Davide

In reply to Chris Hughes

Re: Reducing fractions in a list of assignments

by Davide Cervone -
I found the problem with the answer hints. The AnswerHints macro uses the checker routine to check the student answer against the possible answer choices for the hints, and so when I added the check for the correct answer being an assignment, that meant that the second and third hints would never be marked as a match. So using
    checker => sub {
      my ($correct,$student,$ans,$nth,$value) = @_;
      if ($correct->type eq "Assignment") {
        my ($svar,$sfrac) = $student->value; # get the variable and fraction
        return 0 unless Value::classMatch($sfrac,'Fraction') && $sfrac->isReduced;
      }
      return $correct == $student;
    }
for the checker should fix the problem. I've corrected the code in my previous answer as well.

Davide

In reply to Davide Cervone

Re: Reducing fractions in a list of assignments

by Chris Hughes -
This is wonderful, thanks so much Davide! Some of the mistakes I had left in my code were from my various attempts- thanks for fixing/removing them.

Your answer and explanation has really helped- thanks so much.

Chris