[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 6105 - (download) (as text) (annotate)
Fri Sep 18 17:46:56 2009 UTC (10 years, 2 months ago) by dpvc
File size: 7815 byte(s)
Fixed problem with domain errors

    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 $fID = ANS_NUM_TO_NAME($main::ans_rule_count-1);
   64   my $gID = ANS_NUM_TO_NAME($main::ans_rule_count);
   65   my %ans = composition_ans_list($fID=>$f,$gID=>$g,@_);
   66   ANS($ans{$fID},$ans{$gID});
   67 }
   68 
   69 =head2 NAMED_COMPOSITION_ANS
   70 
   71  NAMED_COMPOSITION_ANS($fID=>$f, $gID=>$g, %options)
   72 
   73 An answer checked to see if $f composed with $g matches a given function, where
   74 $fID and $gID are the names of the answer rules for the functions $f and $g, and
   75 $f and $g are the answers for the functions. %options are any of the options
   76 allowed by composition_ans_list() below.
   77 
   78 This routine allows you to put the answer blanks for $f and $g at any location
   79 in the problem, and in any order.
   80 
   81 Example:
   82 
   83  BEGIN_TEXT
   84  \(g\circ f = (1+x)^2\) when
   85  \(f(x)\) = \{NAMED_ANS('f',20)\} and \(g(x)\) = \{NAMED_ANS('g',20)\}
   86  END_TEXT
   87  NAMED_COMPOSITION_ANS(f => "x^2", g => "1+x");
   88 
   89 =cut
   90 
   91 sub NAMED_COMPOSITION_ANS {NAMED_ANS(composition_ans_list(@_))}
   92 
   93 =head2 composition_ans_list
   94 
   95  composition_ans_list($fID=>$f, $gID=>$g, %options)
   96 
   97 This is an internal routine that returns the named answer checkers
   98 used by COMPOSITION_ANS and NAMED_COMPOSITION_ANS above.
   99 
  100 $fID and $gID are the names of the answer rules for the functions and $f and $g
  101 are the answers for these functions. %options are from among:
  102 
  103 =over
  104 
  105 =item S<C<< var => 'x' >>>
  106 
  107 the name of the variable to use when
  108 both functions use the same one
  109 
  110 =item S<C<< vars => ['x','t'] >>>
  111 
  112 the names of the variables for $f and $g
  113 
  114 =item S<C<< showVariableHints => 1 or 0 >>>
  115 
  116 do/don't show errors when the variable
  117 used by the student is incorrect
  118 
  119 =back
  120 
  121 =cut
  122 
  123 sub composition_ans_list {
  124   my ($fID,$f,$gID,$g,%params) = @_; my @IDs = ($fID,$gID);
  125   #
  126   #  Get options
  127   #
  128   $params{vars} = [$params{var},$params{var}] if $params{var} && !$params{vars};
  129   $params{showVariableHints} = 1 unless defined($params{showVariableHints});
  130   my $isPreview = $main::inputs_ref->{previewAnswers};
  131   my $vars = $params{vars} || [];
  132   my @options = (ignoreInfinity=>0,ignoreStrings=>0);
  133   my ($i,$error);
  134 
  135   #
  136   #  Get correct answer data and determine which variables to use
  137   #
  138   $f = Value->Package("Formula")->new($f); $g = Value->Package("Formula")->new($g);
  139   my %correct = ($fID => $f, $gID => $g);
  140   my %x = ($fID => $vars->[0], $gID => $vars->[1]);
  141   foreach $i (@IDs) {
  142     unless ($x{$i}) {
  143       die "Can't tell which variable to use for $correct{$i}: ".
  144              "use var=>'x' or vars=>['x','y'] to specify it"
  145   if scalar(keys %{$correct{$i}->{variables}}) > 1;
  146       $x{$i} = (keys %{$correct{$i}->{variables}})[0];
  147     }
  148     die "$correct{$i} is not a function of $x{$i}"
  149       unless defined($correct{$i}->{variables}{$x{$i}});
  150   }
  151   my %y = ($fID => $x{$gID}, $gID => $x{$fID});
  152   my %ans = ($fID => message_cmp($f), $gID => message_cmp($g));
  153   my $fog = $f->substitute($x{$fID}=>$g);  #  the composition
  154 
  155   #
  156   #  Check that the student formulas parse OK,
  157   #  produce a number, contain the correct variable,
  158   #  don't contain the composition itself in a simple way,
  159   #  and aren't the identity.
  160   #
  161   my %student = ($fID => $main::inputs_ref->{$fID},
  162      $gID => $main::inputs_ref->{$gID});
  163   foreach $i (@IDs) {
  164     next unless defined($student{$i});
  165     $student{$i} = Parser::Formula($student{$i});
  166     if (!defined($student{$i})) {$error = 1; next}
  167     $ans{$i}->{rh_ans}{preview_latex_string} = $student{$i}->TeX;
  168     if ($student{$i}->type ne 'Number') {
  169       $ans{$i} = $correct{$i}->cmp(@options);
  170       $error = 1; next;
  171     }
  172     if ($x{$fID} ne $x{$gID} && defined($student{$i}->{variables}{$y{$i}})) {
  173       $ans{$i}->{rh_ans}{ans_message} = "Your formula may not contain $y{$i}"
  174   unless $isPreview || !$params{showVariableHints};
  175       $error = 1; next;
  176     }
  177     if (!defined($student{$i}->{variables}{$x{$i}})) {
  178       $ans{$i}->{rh_ans}{ans_message} = "Your formula is not a function of $x{$i}"
  179   unless $isPreview || !$params{showVariableHints};
  180       $error = 1; next;
  181     }
  182     if (($student{$i}->{tree}->class eq 'BOP' &&
  183    ($fog == $student{$i}->{tree}{lop} || $fog == $student{$i}->{tree}{rop})) ||
  184   ($student{$i}->{tree}->class eq 'UOP' && $fog == $student{$i}->{tree}{op})) {
  185       $ans{$i}->{rh_ans}{ans_message} =
  186         "Your formula may not have the composition as one of its terms";
  187       $error = 1; next;
  188     }
  189     if ($fog == $student{$i}) {
  190       $ans{$i}->{rh_ans}{ans_message} =
  191   "Your formula my not be the composition itself";
  192       $error = 1; next;
  193     }
  194     if (Parser::Formula($x{$i}) == $student{$i}) {
  195       $ans{$i}->{rh_ans}{ans_message} = "The identity function is not allowed"
  196   unless $isPreview;
  197       $error = 1; next;
  198     }
  199 
  200   }
  201 
  202   #
  203   #  If no error, and both answers are given, check if compositions are equal
  204   #
  205   if (!$error && defined($student{$fID}) && defined($student{$gID})) {
  206     if ($fog == $student{$fID}->substitute($x{$fID}=>$student{$gID})) {
  207       $ans{$fID}->{rh_ans}{score} = $ans{$gID}->{rh_ans}{score} = 1;
  208     }
  209   }
  210 
  211   return (%ans);
  212 }
  213 
  214 =head2 message_cmp
  215 
  216  message_cmp($correct)
  217 
  218 Returns an answer evaluator that always returns incorrect, with a given error
  219 message. Used by COMPOSITION_ANS to produce "dummy" answer checkers for the two
  220 parts of the composition.
  221 
  222 =cut
  223 
  224 sub message_cmp {
  225   my $correct = shift;
  226   my $answerEvaluator = new AnswerEvaluator;
  227   $answerEvaluator->ans_hash(
  228      type => "message",
  229      correct_ans => $correct->string,
  230      ans_message => $message,
  231   );
  232   $answerEvaluator->install_evaluator(sub {shift});
  233   return $answerEvaluator;
  234 }
  235 
  236 1;
  237 

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9