parserMultiAnswer.pl - Tie several blanks to a single answer checker.
MultiAnswer objects let you tie several answer blanks to a single answer checker, so you can have the answer in one blank influence the answer in another. The MultiAnswer can produce either a single result in the answer results area, or a separate result for each blank.
To create a MultiAnswer pass a list of answers to MultiAnswer() in the order they will appear in the problem. For example:
$mp = MultiAnswer("x^2",-1,1);
or
$mp = MultiAnswer(Vector(1,1,1),Vector(2,2,2))->with(singleResult=>1);
Then, use $mp->ans_rule to create answer blanks for the various parts just as you would ans_rule. You can pass the width of the blank, which defaults to 20 otherwise. For example:
BEGIN_TEXT
\(f(x)\) = \{$mp->ans_rule(20)\} produces the same value
at \(x\) = \{$mp->ans_rule(10)\} as it does at \(x\) = \{$mp->ans_rule(10)\}.
END_TEXT
Finally, call $mp->cmp to produce the answer checker(s) used in the MultiAnswer. You need to provide a checker routine that will be called to determine if the answers are correct or not. The checker will only be called if the student answers have no syntax errors and their types match the types of the professor's answers, so you don't have to worry about handling bad data from the student (at least as far as typechecking goes).
The checker routine should accept four parameters: a reference to the array of correct answers, a reference to the array of student answers, a reference to the MultiAnswer itself, and a reference to the answer hash. It should do whatever checking it needs to do and then return a score for the MultiAnswer as a whole (every answer blank will be given the same score), or a reference to an array of scores, one for each blank. The routine can set error messages via the MultiAnswer's setMessage() method (e.g.,
$mp->setMessage(1,"The function can't be the identity");
would set the message for the first answer blank of the MultiAnswer), or can call Value::Error() to generate an error and die.
The checker routine can be supplied either when the MultiAnswer is created, or when the cmp() method is called. For example:
$mp = MultiAnswer("x^2",1,-1)->with(
singleResult => 1,
checker => sub {
my ($correct,$student,$self) = @_; # get the parameters
my ($f,$x1,$x2) = @{$student}; # extract the student answers
Value::Error("Function can't be the identity") if ($f == 'x');
Value::Error("Function can't be constant") if ($f->isConstant);
return $f->eval(x=>$x1) == $f->eval(x=>$x2);
},
);
ANS($mp->cmp);
or
$mp = MultiAnswer("x^2",1,-1)->with(singleResult=>1);
sub check {
my ($correct,$student,$self) = @_; # get the parameters
my ($f,$x1,$x2) = @{$student}; # extract the student answers
Value::Error("Function can't be the identity") if ($f == 'x');
Value::Error("Function can't be constant") if ($f->isConstant);
return $f->eval(x=>$x1) == $f->eval(x=>$x2);
};
ANS($mp->cmp(checker=>~~&check));
MultiAnswer($answer1, $answer2, ...);
MultiAnswer($answer1, $answer2, ...)->with(...);
Create a new MultiAnswer item from a list of items. The items are converted if Value items, if they aren't already. You can set the following fields of the resulting item:
checker => code a subroutine to be called to check the
student answers. The routine is passed
four parameters: a reference to the array
or correct answers, a reference to the
array of student answers, a reference to the
MultiAnswer object itself, and a reference to
the checker's answer hash. The routine
should return either a score or a reference
to an array of scores (one for each answer).
singleResult => 0 or 1 whether to show only one entry in the
results area at the top of the page,
or one for each answer rule.
(Default: 0)
namedRules => 0 or 1 whether to use named rules or default
rule names. Use named rules if you need
to intersperse other rules with the
ones for the MultiAnswer, in which case
you must use NAMED_ANS not ANS.
(Default: 0)
checkTypes => 0 or 1 whether the types of the student and
professor's answers must match exactly
or just pass the usual type-match error
checking (in which case, you should check
the types before you use the data).
(Default: 1)
allowBlankAnswers=>0 or 1 whether to remove the blank-check prefilter
from the answer checkers used for type
checking the student's answers.
(Default: 0)
separator => string the string to use between entries in the
results area when singleResult is set.
(Default: semicolon)
tex_separator => string same, but for the preview area.
(Default: semicolon followed by thinspace)
format => string an sprintf-style string used to format the
students answers for the results area
when singleResults is true. If undefined,
the separator parameter (above) is used to
form the string.
(Default: undef)
tex_format => string an sprintf-style string used to format the
students answer previews when singleResults
mode is in effect. If undefined, the
tex_separator (above) is used to form the
string.
(Default: undef)