WeBWorK Problems

Mathobjects: changing the context inside an answer evaluator

Mathobjects: changing the context inside an answer evaluator

by Robert Byerly -
Number of replies: 4
At the request of one of our instructors I am trying to solve the following problem. This is probably mostly a question for Davide, but others may have some suggestions too. In particular, someone may have a better approach than the one I'm using.

There are problems that arise naturally in, say, differential equations, in which the solution is an equation in a form such as "f(x,y)=0" or "f(x,y,z)=C", where the left-hand-side is only determined up to a constant multiple, and we would like the student to enter it. However, 0 is always such a constant multiple, and it should specifically be disallowed.


I'm trying to use adaptive parameters. I don't really want an adaptive parameter to show up in the correct answer, since it might be confusing to the student, and I would prefer to have an answer evaluator that anyone could simply drop into a problem and use without having to modify it or figure out how it works. Ideally it shouldn't be restricted to using a fixed set of variables either.

I've tried things like the following:
Context("Numeric")->variables->add(y=>'Real',z=>'Real');

BEGIN_TEXT
problem text
END_TEXT

$ans = Formula("x+y+z");#or any constant multiple

ANS($ans->cmp(checker=>~~&mychecker));

sub mychecker{
my ($correct, $student, $ans)=@_;
#
# add an adaptive parameter "a" to the context
#
$correct=Formula("a*$correct");
return 0 if Formula("$student") == Formula("0"); #don't allow student to enter 0
return $correct == Formula("$student");
}


The question is: how do I add the adaptive parameter to the context? The obvious thing (Context()->variables->add(a=>'Parameter')) doesn't seem to work for me. The only success I've had is by creating a new context inside the answer evaluator, e.g.,

my $newcontext = Context("Numeric");
$newcontext->variables->add(y=>'Real',z=>'Real',a=>'Parameter');

Note that this means I had to re-add the variables y and z. If an instructor used a different set of variables, the answer evaluator would need to be modified.

If I understood what was going on here, I think I would understand contexts a lot better.

Thanks,

Bob Byerly

In reply to Robert Byerly

Re: Mathobjects: changing the context inside an answer evaluator

by Davide Cervone -
Although you didn't say what problem occurred when you added the parameter, I suspect that you got an error message like "Variable 'a' is not defined in this context".

The reason is that, while the answer checker is running, the ability to enter parameters in formulas is disabled (so that students can't enter them in their answers). So if you want to use parameters in a custom answer checker, you have to turn them on again first. Here is one approach

    sub {
      my ($correct,$student,$ans) = @_;
      my $context = Context();
      $context->variables->add(a=>"Parameter");
      $context->flags->set(no_parameters => 0);
      $correct = "a" * $correct;
      return 0 if $student == 0;  # don't allow student to enter 0
      return $correct == $student;
    }
Note that the student answer need not be turned into a formula, as it probably already is, and if not, it will be promoted to one automatically when compared to a formula. There is no need to re-parse the formula two more times when it already has been. Similarly, you can compare the student answer to 0 directly, as it will be promoted to a Formula if needed.

Finally, it is more efficient to use the overloaded multiplication with "a" and $correct, since then the correct answer doesn't have to be re-parsed (the tree will simply be augmented).

Note, however, that this answer checker leaves the context changed (it now has a parameter that it didn't have before). If other answers also use this context, that could affect them (for example, if you used your answer checker again, it would give an error about adding a parameter that already exists).

You could fix this by saving the result rather than returning it, then remove the parameter, and finally return the result.

There is another approach, however, that might be easier, and that would be to make a copy of the current context and add the parameter to that instead. That way, you never modify the original context.

    sub {
      my ($correct,$student,$ans) = @_;
      my $context = Context()->copy;
      $context->variables->add(a=>"Parameter");
      $context->flags->set(no_parameters => 0);
      $correct = "a" * Formula($context,$correct);
      return 0 if $student == 0;  # don't allow student to enter 0
      return $correct == Formula($context,$student);
    }
The one caveat is that you have to coerce the student and correct answers to be in the new context. This is done by supplying a $context parameter to the Formula() command. (You need a relatively recent version of the PG directories, since this feature was added this summer. Otherwise, your Formula("$student") approach would work.)

Note that you still have to enable parameters for the new context, since the context you are copying has them turned off.

Davide

In reply to Davide Cervone

Re: Mathobjects: changing the context inside an answer evaluator

by Robert Byerly -
Thanks, Davide. This is very helpful.

Incidentally, some of my re-parsing that you commented on was due to my getting warnings about comparing formulas from different contexts. Mine was a lazy solution to this.

Bob

In reply to Robert Byerly

Re: Mathobjects: changing the context inside an answer evaluator

by Davide Cervone -
You re-parsing would have been the only way to do it prior to this summer, so it is not a bad idea (and I suspected that might have been the reason).

One other thing to consider: if you are going to can this answer checker so that it is available for others, you may want to use a less likely name for the parameter, in case the author already has a variable called "a". Perhaps something like "aa" or "a00" or even "a0a". Since it will never show up in a message to a student (we hope), you can use an oddball name to prevent name collisions with other variables in use in the problem. A really clever checker would test to see if the name is already defined and change it until it finds one that isn't. I should probably make a utility routine that would do this for you.

Davide
In reply to Davide Cervone

Re: Mathobjects: changing the context inside an answer evaluator

by Nathan Wallach -

This is a very old thread, and the sample code above apparently suffers from the same problems discussed in https://webwork.maa.org/moodle/mod/forum/discuss.php?d=497 . Until today the Wiki page https://webwork.maa.org/mediawiki_new/index.php?title=AdaptiveParameters still had sample code which would lead into those problems. The Wiki page has been updated to use the modified approach provided in the other thread to avoid the issues reported there.

The main change is using 

      if ($self->{_filter_name} ne 'produce_equivalence_message') {
      }
to envelop the adaptive parameter code.