[system] / trunk / pg / macros / answerHints.pl Repository:
ViewVC logotype

View of /trunk/pg/macros/answerHints.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5365 - (download) (as text) (annotate)
Sat Aug 18 23:39:57 2007 UTC (12 years, 6 months ago) by dpvc
File size: 5955 byte(s)
Use alternative method of testing if whether two objects are the same
(without causing them to stringify first).

[Technically, this should call Value::address, the renamed version of
Value::Ref, but I didn't want the AIM participants to have to update
to the latests version of MathObjects to be able to use this.]

    1 sub _answerHints_init {}
    2 
    3 =head1 AnswerHints
    4 
    5 #  This is an answer-checker post-filter that allows you to produce
    6 #  additional error messages for incorrect answers.  You can trigger
    7 #  a message for a single answer, a collection of answers, or via a
    8 #  subroutine that determines the condition for the message.
    9 #
   10 #  Note that this filter only works for MathObjects answer checkers.
   11 #
   12 #  The answer hints are given as a pair using => with the right-hand
   13 #  side being the answer message and the left-hand side being one of
   14 #  three possibilities:  1) the value that triggers the message,
   15 #  2) a reference to an array of values that trigger the message, or
   16 #  3) a code reference to a subtroutine that accepts tthe correct
   17 #  answer, the student's answer, and the answer hash, and returns
   18 #  1 or 0 depending on whether the message should or should not be
   19 #  displayed.  (See the examples below.)
   20 #
   21 #  The right-hand side can be either the message string itself, or
   22 #  a referrence to an array where the first element is the message
   23 #  string, and the remaining elements are name-value pairs that
   24 #  set options for the message.  These can include:
   25 #
   26 #      checkCorrect => 0 or 1      1 means check for messages even
   27 #                                  if the answer is correct.
   28 #                                      Default: 0
   29 #
   30 #      replaceMessage => 0 or 1    1 means it's OK to repalce any
   31 #                                  message that is already in place
   32 #                                  in the answer hash.
   33 #                                      Default: 0
   34 #
   35 #      checkTypes => 0 or 1        1 means only perform the test
   36 #                                  if the student answer is the
   37 #                                  same type as the correct one.
   38 #                                      Default: 1
   39 #
   40 #      score => number             Specifies the score to use if
   41 #                                  the message is triggered (so that
   42 #                                  partial credit can be given).
   43 #                                      Default: keep original score
   44 #
   45 #      cmp_options => [...]        provides options for the cmp routine
   46 #                                  used to check if the student answer
   47 #                                  matches these answers.
   48 #                                     Default: []
   49 #
   50 #  If more than one message matches the student's answer, the first
   51 #  one in the list is used.
   52 #
   53 #  Example:
   54 #
   55 #    ANS(Vector(1,2,3)->cmp(showCoordinateHints=>0)->withPostFilter(AnswerHints(
   56 #      Vector(0,0,0) => "The zero vector is not a valid solution",
   57 #      "-<1,2,3>" => "Try the opposite direction",
   58 #      "<1,2,3>" => "Well done!",
   59 #      ["<1,1,1>","<2,2,2>","<3,3,3>"] => "Don't just guess!",
   60 #      sub {
   61 #        my ($correct,$student,$ans) = @_;
   62 #        return $correct . $student == 0;
   63 #      } => "Your answer is perpendicular to the correct one",
   64 #      Vector(1,2,3) => [
   65 #        "You have the right direction, but not length",
   66 #        cmp_options => [parallel=>1],
   67 #      ],
   68 #      0 => ["Careful, your answer should be a vector!", checkTypes => 0, replaceMessage => 1],
   69 #      sub {
   70 #        my ($correct,$student,$ans) = @_;
   71 #        return norm($correct-$student) < .1;
   72 #      } => ["Close!  Keep trying.", score => .25],
   73 #    )));
   74 #
   75 
   76 =cut
   77 
   78 sub AnswerHints {
   79   return (sub {
   80     my $ans = shift; $ans->{_filter_name} = "Answer Hints Post Filter";
   81     my $correct = $ans->{correct_value};
   82     my $student = $ans->{student_value};
   83     Value::Error("AnswerHints can only be used with MathObjects answer checkers") unless ref($correct);
   84     return $ans unless ref($student);
   85 
   86     while (@_) {
   87       my $wrongList = shift; my $message = shift; my @options;
   88       ($message,@options) = @{$message} if ref($message) eq 'ARRAY';
   89       my %options = (
   90         checkCorrect => 0,
   91         replaceMessage => 0,
   92   checkTypes => 1,
   93         score => undef,
   94         cmp_options => [],
   95         @options,
   96       );
   97       next if $options{checkType} && $correct->type ne $student->type;
   98       $wrongList = [$wrongList] unless ref($wrongList) eq 'ARRAY';
   99       foreach my $wrong (@{$wrongList}) {
  100   if (ref($wrong) eq 'CODE') {
  101     if (($ans->{score} < 1 || $options{checkCorrect}) &&
  102         ($ans->{ans_message} eq "" || $options{replaceMessage}) &&
  103         &$wrong($correct,$student,$ans)) {
  104       $ans->{ans_message} = $ans->{error_message} = $message;
  105       $ans->{score} = $options{score} if defined $options{score};
  106       last;
  107     }
  108   } else {
  109     $wrong = Value::makeValue($wrong);
  110     if (($ans->{score} < 1 || $options{checkCorrect} || AnswerHints::Compare($correct,$wrong,$ans)) &&
  111         ($ans->{ans_message} eq "" || $options{replaceMessage}) &&
  112         AnswerHints::Compare($wrong,$student,$ans,@{$options{cmp_options}})) {
  113       $ans->{ans_message} = $ans->{error_message} = $message;
  114       $ans->{score} = $options{score} if defined $options{score};
  115       last;
  116     }
  117   }
  118       }
  119     }
  120     return $ans;
  121   },@_);
  122 }
  123 
  124 package AnswerHints;
  125 
  126 #
  127 #  Calls the answer checker on two values with a copy of the answer hash
  128 #  and returns true if the two values match and false otherwise.
  129 #
  130 sub Compare {
  131   my $self = shift; my $other = shift; my $ans = shift;
  132   $ans = bless {%{$ans},@_}, ref($ans);  # make a copy
  133   $ans->{typeError} = 0; $ans->{ans_message} = $ans->{error_message} = ""; $ans->{score} = 0;
  134   if (sprintf("%p",$self) ne sprintf("%p",$ans->{correct_value})) {
  135     $ans->{correct_ans} = $self->string;
  136     $ans->{correct_value} = $self;
  137     $ans->{correct_formula} = Value->Package("Formula")->new($self);
  138   }
  139   if (sprintf("%p",$other) ne sprintf("%p",$ans->{student_value})) {
  140     $ans->{student_ans} = $other->string;
  141     $ans->{student_value} = $other;
  142     $ans->{student_formula} = Value->Package("Formula")->new($other);
  143   }
  144   $self->cmp_preprocess($ans);
  145   $self->cmp_equal($ans);
  146   $self->cmp_postprocess($ans) if !$ans->{error_message} && !$ans->{typeError};
  147   return $ans->{score} >= 1;
  148 }
  149 
  150 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9