John:
I hadn't thought of the factorization you suggested, and agree that it
should be handled. So I rewrote my MultiPart example as follows: Context("Numeric");
$mp = MultiPart("x+1","x-1")->with( checker => sub { my $correct = shift; my $student = shift; my $self = shift; my ($F,$G) = @{$correct}; my ($f,$g) = @{$student}; Value::Error('Neither factor can be constant') unless $f->isFormula && $g->isFormula; return 0 unless $F*$G == $f*$g; Context()->variables->add(a=>'Parameter'); my $result = ($F*'a' == $f || $F*'a' == $g); Context()->variables->remove('a'); return $result; }, singleResult => 1, );
BEGIN_TEXT Factor: \(x^2-1\) = \{$mp->ans_rule(15)\} \(\times\) \{$mp->ans_rule(15)\}. END_TEXT
ANS($mp->cmp);
Here, we multiply the two factors from the student and professors
answers and compare the results before looking at the individual
factors. If the products are OK, we add a parameter to the context and
do an adaptive comparison between a multiple of the professor's first
factor and the student's answers. If either matches, then since the
product also matched, the remaining factor will match as well.
Alternatively, you ask about how to use the List checker, using a
single answer rule and separating the factors by a comma. Here is one
solution: Context("Numeric");
$L = List(Formula("x-1"),Formula("x+1"));
BEGIN_TEXT List the factors of \(x^2-1\) separated by commas \{ans_rule(40)\} END_TEXT
ANS($L->cmp( checker => sub { my $correct = shift; my $student = shift; return 0 unless $correct->isFormula && $student->isFormula; return $correct*'a' == $student; }, list_checker => sub { my $context = $L->{context}; $context->variables->add(a=>'Parameter'); $context->flags->set(no_parameters => 0); my ($score,@errors) = $L->cmp_list_compare(@_); $context->flags->set(no_parameters => 1); $context->variables->remove('a'); my ($correct,$student,$ans) = @_; if ($score == scalar(@{$correct}) && $score == scalar(@{$student})) { my $F = Formula('1'); my $f = Formula('1'); my $h; foreach $h (@{$correct}) {$F = $F*$h} foreach $h (@{$student}) {$f = $f*$h} $score = 0 unless $F == $f; } return ($score,@errors); }, showLengthHints => 1, ));
This one supplies both the list entry checker (via checker ) and a global List checker (via list_checker ). Here is how it works: the list_checker
is called with a a reference to an array of the parsed correct answers,
a reference to an array of the parsed student answers, and some other
data we don't use in this case. In this routine, we install the
parameter 'a' and allow parameters to be used in formulas (they are
turned off during student input and haven't been turned back on yet).
Then we call the standard list checker by hand. That list checker
handles doing the unordered comparison, adding up scores, reporting
errors, and so on, and most important, it calls our checker subroutine in order to compare professor and student answers. Our version of the checker
routine gets the correct and student answers and checks that they
actually are formulas, then does the adaptive comparison to see if one
is a multiple of the other. If it is, it will be matched as correct
(provisionally).
Once the standard list checker completes, we get back a score and a
list of error messages (if any). The score is the total number of
matched items in the list, so if the student matched each factor, then
she has gotten them all correct up to a constant multiple, so we still
need to check if the products are equal (she could still be off by a
constant multiple). To check this, we loop through the correct and
student answers and make a product of all the factors in each list, and
then compare the results. If they aren't equal, we set the score to
zero, otherwise, they have the right answer. In the former case, you
might also want to add a message to the error list indicating that the
result is off by a constant mutliple (unless $ans->{isPreview} is set).
The List answer checker can also take additional parameters like showLengthHints that indicate if messages should be issued when the student's answers are all correct but there are some missing, and showHints ,
which controls whether entries in the list should be marked as
incorrect individually (so the student knows which entries he got
right).
Hope that covers all the bases.
Davide
<| Post or View Comments |>
|