One approach is to use the following subroutine:
sub blank_cmp { my $cmp = str_cmp("",@_); $cmp->install_pre_filter('erase'); $cmp->install_pre_filter(~~&remove_whitespace); $cmp; }
and then use ANS(blank_cmp) in place of the str_cmp
calls that you have been using. You will still have the issue of the
warning message that WeBWorK will issue if there are blank entries. A
little hack to disable that is to include
TEXT(MODES(HTML=>"<STYLE>div.ResultsAlert {display:none}</STYLE>",tex=>''));
after the TEXT(beginproblem) somewhere. This will make the warning message disappear (though it may still show up for a moment as the problemis loaded).On
the other hand, I don't think your approach to this problem is the most
effective one. Let me address some of the comments you have made above:
One reason I like the empty boxes is that one number per box means they get credit for each correct prime that is in order.
Since WeBWorK handles partial credit within a single answer, a list-based answer checker can do this as well.
The developmental students who see this problem are likely to be
confused by syntax for entering a list into a single answer box.
I find it hard to belive that students who are capable of understanding
prime numbers can't figure out how to type commas between their
answers. Is that really what you are suggesting? My experience with our
students (admittedly not developmental students) is that they are far
less comfortable about leaving answers blank than about entering lists.
if they submit the answers with empty boxes they see how many primes there are
Then what is the purpose of having 100 boxes to start with? Why not
just give them the right number initially, since they can find out how
many there are by just hitting the submit button. Is their not having
that information on their first attempt really important? I'm not sure
the overhead of having 100 boxes (and worse, 100 rows in the results
table at the top of the page when they submit answers) is worth the
marginal value of letting them think that all 100 might be prime. I
would think that having to scroll past over seventy blank rows of empty
preview boxes in order to get down to the entry blanks again would be a
source of frustration (it would be for me), especially if they had to
scroll back up to the top to read the error messages again.
they can use that information to refine and improve on their next attempt
This would also be true for a list-based checker. Here is an example of
how this could be done. This one is fairly sophisticated, and tries to
model the effect that you are getting, but without the need for 100
entry blanks. It will give credit for the correct primes up to the
first incorrect entry in the list. Furthermore, it will tell you
helpful information about the first wrong entry.
(I had an earlier version that gave credit for any correct primes, and told you which entries in teh list were incorrect, but this would mean you could just enter every
number from 2 to 99 and WW would tell you exactly which are primes. You
can still get WW to tell you the right answers by entering successive
integers until it tells you you got the next one, but that is also true
of your implementation as well.)
DOCUMENT(); # This should be the first executable line in the problem.
loadMacros( "PG.pl", "PGbasicmacros.pl", "PGanswermacros.pl", "Parser.pl", );
TEXT(beginproblem);
Context("Numeric");
$Primes = List(2,3,5,7,11,13,17,19,23,29,31,37,41,43,47, 53,59,61,67,71,73,79,83,89,91,97);
BEGIN_TEXT
$BCENTER List all the prime numbers that are less than 100: $PAR
\{ans_box(3,60)\} $PAR
${BITALIC}(Enter you answers in increasing order, separated by commas.)${EITALIC} $ECENTER
END_TEXT
ANS($Primes->cmp(showLengthHints => 0, list_checker => sub { my $correct = shift; my $student = shift; my $ans = shift; my @correct = @{$correct}; my @student = @{$student}; my $n = scalar(@student); my $m = scalar(@correct); my $score = 0; my @errors; my $i;
# # Split preview so it is not too long # if ($n > 8) { my @rows; $i = 0; while ($i < $n) {push(@rows,join(",\,",@student[$i..min($i+7,$n-1)])); $i += 8} $ans->{preview_latex_string} = "\begin{array}{c}".join(",\\",@rows)."\end{array}"; }
# # Check that everything is a positive integer # for ($i = 0; $i < $n; $i++) { if ($student[$i]->class ne 'Real') { push(@errors,"Your ".$Primes->NameForNumber($i+1). " answer is not a Number (it looks like ".Value::showClass($student[$i]).")"); } else { $student[$i] = $student[$i]->value; if ($student[$i] !~ m/^-?[0-9]+$/) { push(@errors,"$student[$i] is not an integer"); } elsif ($student[$i] < 1) { push(@errors,"$student[$i] is not a positive integer"); } elsif ($student[$i] > 100) { push(@errors,"$student[$i] is bigger than 100"); } } } return ($score,@errors) if scalar(@errors);
# # Check for numbers being in ascending order # for ($i = 0; $i < $n-1; $i++) { if ($student[$i] > $student[$i+1]) { push(@errors,"Your numbers are not listed in increasing order") unless $ans->{isPreview}; return ($score,@errors); } }
# # Find the first wrong number # $i = 0; while ($i < $n && $i < $m) { if ($student[$i] != $correct[$i]) { unless ($ans->{isPreview}) { if ($student[$i] < $correct[$i]) { push(@errors,"$student[$i] is not prime"); } elsif ($i == 0) { push(@errors,"There is a prime before $student[$i]"); } else { push(@errors,"There is prime between $student[$i-1] and $student[$i]"); } } return($score,@errors); } $i++; $score++; } if (!$ans->{isPreview}) { push(@errors,"There are more primes after $student[$n-1]") if $i < $m; push(@errors,"$student[$i] is not prime") if $i < $n; }
return ($score,@errors); }));
$showPartialCorrectAnswers = 1;
##############################################
END_PROBLEM(); ENDDOCUMENT(); # This should be the last executable line in the problem.
This creates a List object with the correct answers, and an answer box
for the students to type in their list. The answer checker uses a
custom list checker to implement the special rules for this problem.
The checker routine gets the data about the correct answers and the
student answers. The first thing that happens is we modify the answer
preview to break it into several lines (otherwise the preview will get
too wide and make the results table too wide). The next thing is to run
through the student answers to check that they actually are numbers,
and that the numbers are positive integers. Appropirate error message
are issued for any entries that are not positive integers.
Next, we check that the student answers are in ascending order, and report an error if not.
Then we look through the student and correct answers to find the first
number that is not the same (i.e., the first wrong entry). An error is
issued either pointing out that the student answer isn't prime, or that
there are primes missing before the students number.
Finally, we report errors for any extra numbers the student has
entered, or about missing primes at the end if the student has entered
a correct , but too short, list.
This example has all the features that your original does, but has the
advantage of not taking up so much screen space, and of giving more
informative error messages.
Davide
PS, it turns out the the MultiPart object can't be used for this after
all (at least not currently). I'll look into what changes must be made
to allow the Parser (and so the MultiPart object) to handle blank
answers.
<| Post or View Comments |>
|