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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2732 - (download) (as text) (annotate)
Sat Sep 4 20:42:45 2004 UTC (15 years, 3 months ago) by dpvc
File size: 7076 byte(s)
This file provides an answer checker that determines whether two
students answers compose to form a given function.  The student
answers are not allowed to be the identity function, or a function
that is trivially generated from the composition.

See the comments within the file for details about how to use it.

    1 ######################################################################
    2 #
    3 #  An answer checker that determines if two functions compose
    4 #  to form a given function.  (For use in problems where you ask
    5 #  a student to break a given function into a composition of two
    6 #  simpler functions, neither of which is allowed to be the identity
    7 #  function.)
    8 #
    9 
   10 sub _answerComposition_init {}; # don't reload this file
   11 
   12 ######################################################################
   13 #
   14 #  An answer checked to see if f composed with g matches a given function.
   15 #
   16 #  Usage:  COMPOSITION_ANS(f,g,options)
   17 #
   18 #  where f and g are one possible decomposition of the target function
   19 #  (these are used to display the "correct" answer, and the composition
   20 #  is computed from them) and options are any of the options allowed
   21 #  by composition_ans_list() below.
   22 #
   23 #  This function actually supplies TWO answer checkers, for the two
   24 #  previous answer blanks.  So be sure to call it immediately after
   25 #  the answer blanks have been supplied.  (It may be best to use the
   26 #  NAMED_COMPOSITION_ANS checker below, which specifies the answer
   27 #  blanks explicitly.)
   28 #
   29 #  Example:
   30 #
   31 #     BEGIN_TEXT
   32 #       \(f\circ g = (1+x)^2\) when
   33 #       \(f(x)\) = \{ans_rule(20)\} and \(g(x)\) = \{ans_rule(20)\}
   34 #     END_TEXT
   35 #     COMPOSITION_ANS("x^2","1+x");
   36 #
   37 #
   38 sub COMPOSITION_ANS {
   39   my $f = shift; my $g = shift;
   40   my $fID = ANS_NUM_TO_NAME($main::ans_rule_count-1);
   41   my $gID = ANS_NUM_TO_NAME($main::ans_rule_count);
   42   my %ans = composition_ans_list($fID=>$f,$gID=>$g,@_);
   43   ANS($ans{$fID},$ans{$gID});
   44 }
   45 
   46 
   47 ######################################################################
   48 #
   49 #  An answer checked to see if f composed with g matches a given function.
   50 #
   51 #  Usage:  NAMED_COMPOSITION_ANS(fID=>f,gID->g,options)
   52 #
   53 #  where fID and gID are the names of the answer rules for the functions
   54 #  f and g, and f and g are the answers for the functions.  Options are
   55 #  any of the options allowed by composition_ans_list() below.
   56 #
   57 #  This routine allows you to put the f and g answer blanks at any
   58 #  location in the problem, and in any order.
   59 #
   60 #  Example:
   61 #
   62 #     BEGIN_TEXT
   63 #       \(g\circ f = (1+x)^2\) when
   64 #       \(f(x)\) = \{NAMED_ANS('f',20)\} and \(g(x)\) = \{NAMED_ANS('g',20)\}
   65 #     END_TEXT
   66 #     NAMED_COMPOSITION_ANS(f => "x^2", g => "1+x");
   67 #
   68 sub NAMED_COMPOSITION_ANS {NAMED_ANS(composition_ans_list(@_))}
   69 
   70 ######################################################################
   71 #
   72 #  This is an internal routine that returns the named answer checkers
   73 #  used by COMPOSITION_ANS and NAMED_COMPOSITION_ANS above.
   74 #
   75 #  Usage:  composition_ans_list(fID=>f,gID=>g,options)
   76 #
   77 #  where fID and gID are the names of the answer rules for the functions
   78 #  and f and g are the answers for these functions.  Options are from
   79 #  among:
   80 #
   81 #      var => 'x'              the name of the variable to use when
   82 #                              both functions use the same one
   83 #
   84 #      vars => ['x','t']       the names of the variabless for f and g
   85 #
   86 #      showVariableHints => 1 or 0
   87 #                              do/don't show errors when the variable
   88 #                              used by the student is incorrect
   89 #
   90 
   91 sub composition_ans_list {
   92   my ($fID,$f,$gID,$g,%params) = @_; my @IDs = ($fID,$gID);
   93   #
   94   #  Get options
   95   #
   96   $params{vars} = [$params{var},$params{var}] if $params{var} && !$params{vars};
   97   $params{showVariableHints} = 1 unless defined($params{showVariableHints});
   98   my $isPreview = $main::inputs_ref->{previewAnswers};
   99   my $vars = $params{vars} || [];
  100   my @options = (ignoreInfinity=>0,ignoreStrings=>0);
  101   my ($i,$error);
  102 
  103   #
  104   #  Get correct answer data and determine which variables to use
  105   #
  106   $f = Value::Formula->new($f); $g = Value::Formula->new($g);
  107   my %correct = ($fID => $f, $gID => $g);
  108   my %x = ($fID => $vars->[0], $gID => $vars->[1]);
  109   foreach $i (@IDs) {
  110     unless ($x{$i}) {
  111       die "Can't tell which variable to use for $correct{$i}: ".
  112              "use var=>'x' or vars=>['x','y'] to specify it"
  113   if scalar(keys %{$correct{$i}->{variables}}) > 1;
  114       $x{$i} = (keys %{$correct{$i}->{variables}})[0];
  115     }
  116     die "$correct{$i} is not a function of $x{$i}"
  117       unless defined($correct{$i}->{variables}{$x{$i}});
  118   }
  119   my %y = ($fID => $x{$gID}, $gID => $x{$fID});
  120   my %ans = ($fID => message_cmp($f), $gID => message_cmp($g));
  121   my $fog = $f->substitute($x{$fID}=>$g);  #  the composition
  122 
  123   #
  124   #  Check that the student formulas parse OK,
  125   #  produce a number, contain the correct variable,
  126   #  don't contain the composition itself in a simple way,
  127   #  and aren't the identity.
  128   #
  129   my %student = ($fID => $main::inputs_ref->{$fID},
  130      $gID => $main::inputs_ref->{$gID});
  131   foreach $i (@IDs) {
  132     next unless defined($student{$i});
  133     $student{$i} = Parser::Formula($student{$i});
  134     if (!defined($student{$i})) {$error = 1; next}
  135     $ans{$i}->{rh_ans}{preview_latex_string} = $student{$i}->TeX;
  136     if ($student{$i}->type ne 'Number') {
  137       $ans{$i} = $correct{$i}->cmp(@options);
  138       $error = 1; next;
  139     }
  140     if ($x{$fID} ne $x{$gID} && defined($student{$i}->{variables}{$y{$i}})) {
  141       $ans{$i}->{rh_ans}{ans_message} = "Your formula may not contain $y{$i}"
  142   unless $isPreview || !$params{showVariableHints};
  143       $error = 1; next;
  144     }
  145     if (!defined($student{$i}->{variables}{$x{$i}})) {
  146       $ans{$i}->{rh_ans}{ans_message} = "Your formula is not a function of $x{$i}"
  147   unless $isPreview || !$params{showVariableHints};
  148       $error = 1; next;
  149     }
  150     if (($student{$i}->{tree}->class eq 'BOP' &&
  151    ($fog == $student{$i}->{tree}{lop} || $fog == $student{$i}->{tree}{rop})) ||
  152   ($student{$i}->{tree}->class eq 'UOP' && $fog == $student{$i}->{tree}{op})) {
  153       $ans{$i}->{rh_ans}{ans_message} =
  154         "Your formula may not have the composition as one of its terms";
  155       $error = 1; next;
  156     }
  157     if ($fog == $student{$i}) {
  158       $ans{$i}->{rh_ans}{ans_message} =
  159   "Your formula my not be the composition itself";
  160       $error = 1; next;
  161     }
  162     if ($student{$i} == $x{$i}) {
  163       $ans{$i}->{rh_ans}{ans_message} = "The identity function is not allowed"
  164   unless $isPreview;
  165       $error = 1; next;
  166     }
  167 
  168   }
  169 
  170   #
  171   #  If no error, nd both answers are given, check if compositions are equal
  172   #
  173   if (!$error && defined($student{$fID}) && defined($student{$gID})) {
  174     if ($fog == $student{$fID}->substitute($x{$fID}=>$student{$gID})) {
  175       $ans{$fID}->{rh_ans}{score} = $ans{$gID}->{rh_ans}{score} = 1;
  176     }
  177   }
  178 
  179   return (%ans);
  180 }
  181 
  182 
  183 ######################################################################
  184 #
  185 #  Evaluator that always returns incorrect, with a given error
  186 #  message.  Used by COMPOSITION_ANS to produce "dummy" answer
  187 #  checkers for the two parts of the composition.
  188 #
  189 sub message_cmp {
  190   my $correct = shift;
  191   my $answerEvaluator = new AnswerEvaluator;
  192   $answerEvaluator->ans_hash(
  193      type => "message",
  194      correct_ans => $correct->string,
  195      ans_message => $message,
  196   );
  197   $answerEvaluator->install_evaluator(sub {shift});
  198   return $answerEvaluator;
  199 }
  200 
  201 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9