[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 6590 - (download) (as text) (annotate)
Wed Dec 1 21:29:54 2010 UTC (9 years, 1 month ago) by gage
File size: 7850 byte(s)
changed references to  $ans_rule_count  to ans_rule_count();



    1 ################################################################################
    2 # WeBWorK Online Homework Delivery System
    3 # Copyright  2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
    4 # $CVSHeader: pg/macros/answerComposition.pl,v 1.8 2009/06/25 23:28:44 gage Exp $
    5 #
    6 # This program is free software; you can redistribute it and/or modify it under
    7 # the terms of either: (a) the GNU General Public License as published by the
    8 # Free Software Foundation; either version 2, or (at your option) any later
    9 # version, or (b) the "Artistic License" which comes with this package.
   10 #
   11 # This program is distributed in the hope that it will be useful, but WITHOUT
   12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   13 # FOR A PARTICULAR PURPOSE.  See either the GNU General Public License or the
   14 # Artistic License for more details.
   15 ################################################################################
   16 
   17 =head1 NAME
   18 
   19 answerComposition.pl - An answer checker that determines if two functions
   20 compose to form a given function.
   21 
   22 =head1 DESCRIPTION
   23 
   24 answerComposition.pl provides an answer checker that determines if two functions
   25 compose to form a given function. This can be used in problems where you ask a
   26 student to break a given function into a composition of two simpler functions,
   27 neither of which is allowed to be the identity function.
   28 
   29 =cut
   30 
   31 sub _answerComposition_init {}; # don't reload this file
   32 
   33 =head1 MACROS
   34 
   35 =head2 COMPOSITION_ANS
   36 
   37   COMPOSITION_ANS($f, $g, %options)
   38 
   39 An answer checked to see if $f composed with $g matches a given function,where
   40 $f and $g are one possible decomposition of the target function, and options are
   41 any of the options allowed by composition_ans_list() below.
   42 
   43 $f and $g are used to display the "correct" answer, and the composition is
   44 computed from them.
   45 
   46 This function actually supplies TWO answer checkers, for the two previous answer
   47 blanks.  So be sure to call it immediately after the answer blanks have been
   48 supplied. (It may be best to use the NAMED_COMPOSITION_ANS checker below, which
   49 specifies the answer blanks explicitly.)
   50 
   51 Example:
   52 
   53   BEGIN_TEXT
   54   \(f\circ g = (1+x)^2\) when
   55   \(f(x)\) = \{ans_rule(20)\} and \(g(x)\) = \{ans_rule(20)\}
   56   END_TEXT
   57   COMPOSITION_ANS("x^2","1+x");
   58 
   59 =cut
   60 
   61 sub COMPOSITION_ANS {
   62   my $f = shift; my $g = shift;
   63   my $num_of_answers = main::ans_rule_count();
   64   my $fID = ANS_NUM_TO_NAME($num_of_answers-1);
   65   my $gID = ANS_NUM_TO_NAME($num_of_answers);
   66   my %ans = composition_ans_list($fID=>$f,$gID=>$g,@_);
   67   ANS($ans{$fID},$ans{$gID});
   68 }
   69 
   70 =head2 NAMED_COMPOSITION_ANS
   71 
   72  NAMED_COMPOSITION_ANS($fID=>$f, $gID=>$g, %options)
   73 
   74 An answer checked to see if $f composed with $g matches a given function, where
   75 $fID and $gID are the names of the answer rules for the functions $f and $g, and
   76 $f and $g are the answers for the functions. %options are any of the options
   77 allowed by composition_ans_list() below.
   78 
   79 This routine allows you to put the answer blanks for $f and $g at any location
   80 in the problem, and in any order.
   81 
   82 Example:
   83 
   84  BEGIN_TEXT
   85  \(g\circ f = (1+x)^2\) when
   86  \(f(x)\) = \{NAMED_ANS('f',20)\} and \(g(x)\) = \{NAMED_ANS('g',20)\}
   87  END_TEXT
   88  NAMED_COMPOSITION_ANS(f => "x^2", g => "1+x");
   89 
   90 =cut
   91 
   92 sub NAMED_COMPOSITION_ANS {NAMED_ANS(composition_ans_list(@_))}
   93 
   94 =head2 composition_ans_list
   95 
   96  composition_ans_list($fID=>$f, $gID=>$g, %options)
   97 
   98 This is an internal routine that returns the named answer checkers
   99 used by COMPOSITION_ANS and NAMED_COMPOSITION_ANS above.
  100 
  101 $fID and $gID are the names of the answer rules for the functions and $f and $g
  102 are the answers for these functions. %options are from among:
  103 
  104 =over
  105 
  106 =item S<C<< var => 'x' >>>
  107 
  108 the name of the variable to use when
  109 both functions use the same one
  110 
  111 =item S<C<< vars => ['x','t'] >>>
  112 
  113 the names of the variables for $f and $g
  114 
  115 =item S<C<< showVariableHints => 1 or 0 >>>
  116 
  117 do/don't show errors when the variable
  118 used by the student is incorrect
  119 
  120 =back
  121 
  122 =cut
  123 
  124 sub composition_ans_list {
  125   my ($fID,$f,$gID,$g,%params) = @_; my @IDs = ($fID,$gID);
  126   #
  127   #  Get options
  128   #
  129   $params{vars} = [$params{var},$params{var}] if $params{var} && !$params{vars};
  130   $params{showVariableHints} = 1 unless defined($params{showVariableHints});
  131   my $isPreview = $main::inputs_ref->{previewAnswers};
  132   my $vars = $params{vars} || [];
  133   my @options = (ignoreInfinity=>0,ignoreStrings=>0);
  134   my ($i,$error);
  135 
  136   #
  137   #  Get correct answer data and determine which variables to use
  138   #
  139   $f = Value->Package("Formula")->new($f); $g = Value->Package("Formula")->new($g);
  140   my %correct = ($fID => $f, $gID => $g);
  141   my %x = ($fID => $vars->[0], $gID => $vars->[1]);
  142   foreach $i (@IDs) {
  143     unless ($x{$i}) {
  144       die "Can't tell which variable to use for $correct{$i}: ".
  145              "use var=>'x' or vars=>['x','y'] to specify it"
  146   if scalar(keys %{$correct{$i}->{variables}}) > 1;
  147       $x{$i} = (keys %{$correct{$i}->{variables}})[0];
  148     }
  149     die "$correct{$i} is not a function of $x{$i}"
  150       unless defined($correct{$i}->{variables}{$x{$i}});
  151   }
  152   my %y = ($fID => $x{$gID}, $gID => $x{$fID});
  153   my %ans = ($fID => message_cmp($f), $gID => message_cmp($g));
  154   my $fog = $f->substitute($x{$fID}=>$g);  #  the composition
  155 
  156   #
  157   #  Check that the student formulas parse OK,
  158   #  produce a number, contain the correct variable,
  159   #  don't contain the composition itself in a simple way,
  160   #  and aren't the identity.
  161   #
  162   my %student = ($fID => $main::inputs_ref->{$fID},
  163      $gID => $main::inputs_ref->{$gID});
  164   foreach $i (@IDs) {
  165     next unless defined($student{$i});
  166     $student{$i} = Parser::Formula($student{$i});
  167     if (!defined($student{$i})) {$error = 1; next}
  168     $ans{$i}->{rh_ans}{preview_latex_string} = $student{$i}->TeX;
  169     if ($student{$i}->type ne 'Number') {
  170       $ans{$i} = $correct{$i}->cmp(@options);
  171       $error = 1; next;
  172     }
  173     if ($x{$fID} ne $x{$gID} && defined($student{$i}->{variables}{$y{$i}})) {
  174       $ans{$i}->{rh_ans}{ans_message} = "Your formula may not contain $y{$i}"
  175   unless $isPreview || !$params{showVariableHints};
  176       $error = 1; next;
  177     }
  178     if (!defined($student{$i}->{variables}{$x{$i}})) {
  179       $ans{$i}->{rh_ans}{ans_message} = "Your formula is not a function of $x{$i}"
  180   unless $isPreview || !$params{showVariableHints};
  181       $error = 1; next;
  182     }
  183     if (($student{$i}->{tree}->class eq 'BOP' &&
  184    ($fog == $student{$i}->{tree}{lop} || $fog == $student{$i}->{tree}{rop})) ||
  185   ($student{$i}->{tree}->class eq 'UOP' && $fog == $student{$i}->{tree}{op})) {
  186       $ans{$i}->{rh_ans}{ans_message} =
  187         "Your formula may not have the composition as one of its terms";
  188       $error = 1; next;
  189     }
  190     if ($fog == $student{$i}) {
  191       $ans{$i}->{rh_ans}{ans_message} =
  192   "Your formula my not be the composition itself";
  193       $error = 1; next;
  194     }
  195     if (Parser::Formula($x{$i}) == $student{$i}) {
  196       $ans{$i}->{rh_ans}{ans_message} = "The identity function is not allowed"
  197   unless $isPreview;
  198       $error = 1; next;
  199     }
  200 
  201   }
  202 
  203   #
  204   #  If no error, and both answers are given, check if compositions are equal
  205   #
  206   if (!$error && defined($student{$fID}) && defined($student{$gID})) {
  207     if ($fog == $student{$fID}->substitute($x{$fID}=>$student{$gID})) {
  208       $ans{$fID}->{rh_ans}{score} = $ans{$gID}->{rh_ans}{score} = 1;
  209     }
  210   }
  211 
  212   return (%ans);
  213 }
  214 
  215 =head2 message_cmp
  216 
  217  message_cmp($correct)
  218 
  219 Returns an answer evaluator that always returns incorrect, with a given error
  220 message. Used by COMPOSITION_ANS to produce "dummy" answer checkers for the two
  221 parts of the composition.
  222 
  223 =cut
  224 
  225 sub message_cmp {
  226   my $correct = shift;
  227   my $answerEvaluator = new AnswerEvaluator;
  228   $answerEvaluator->ans_hash(
  229      type => "message",
  230      correct_ans => $correct->string,
  231      ans_message => $message,
  232   );
  233   $answerEvaluator->install_evaluator(sub {shift});
  234   return $answerEvaluator;
  235 }
  236 
  237 1;
  238 

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9