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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3196 - (download) (as text) (annotate)
Wed Mar 16 13:40:29 2005 UTC (14 years, 10 months ago) by dpvc
File size: 6157 byte(s)
Implements an answer evaluator that checks if a students answer
satisfies a given equality.  See the comments in the file for details
about how to use the answer checker.

    1 loadMacros("Parser.pl");
    2 
    3 sub _parserSolutionFor_init {}; # don't reload this file
    4 
    5 ######################################################################
    6 #
    7 #  This is a Parser class that implements an answer checker that
    8 #  checks if a student's answer satisfies an implicit equation.
    9 #  We define a SolutionFor object class that lets you specify an
   10 #  equality that the student answer must satisfy, and a point that
   11 #  DOES satify the equation.  The overloaded == operator will
   12 #  check if a given point satisfies the given equality.
   13 #
   14 #  Use SolutionFor(equality,point[,options]) to create a SolutionFor object.
   15 #  The equality is a Formula object containing an equality, or a string
   16 #  representing such a formula, and the point is a Point object or string
   17 #  containing a point that satisfies the equation (to be used as the
   18 #  correct answer when the student asks to see the answers).
   19 #
   20 #  The variables to use are declared in the Context in the usual way,
   21 #  and the coordinates of the student point will be considered to be in
   22 #  alphabetical order.  You can override this by supplying the vars=>[...]
   23 #  option, where you specify the variable names in the order you want the
   24 #  student to give them.  E.g., vars=>['y','x'] will make the student answer
   25 #  represent the point (y,x) rather than the default (x,y).
   26 #
   27 #  Usage examples:
   28 #
   29 #     Context("Vector")->variables->are(x=>'Real',y=>'real');
   30 #     $f = SolutionFor("x^2 = cos(y)","(1,0)");
   31 #     $f = SolutionFor("x^2 - y = 0",[2,4]);
   32 #     $f = SolutionFor("x^2 - y = 0",Point(4,2),vars=>['y','x']);
   33 #
   34 #  Then use
   35 #
   36 #     ANS($f->cmp);
   37 #
   38 #  to get the answer checker for $f.
   39 #
   40 #  You can use $f->{f} to get the Formula object for the equality used
   41 #  in the object, and $f->f(point) to determine if the given point is
   42 #  a solution to the equality or not.  For example, if you want to include
   43 #  the TeX version of a formula within the text of a problem, you can use:
   44 #
   45 #     Context()->texStrings;
   46 #     BEGIN_TEXT
   47 #       A solution to \($f->{f}\) is \((x,y)\) = \{ans_rule(30)\}.
   48 #     END_TEXT
   49 #     Context()->normalStrings;
   50 #     ANS($f->cmp);
   51 #
   52 ######################################################################
   53 
   54 #
   55 #  Create a SolutionFor object of the correct type
   56 #
   57 sub SolutionFor {
   58   #
   59   #  Get the professor's equation
   60   #
   61   my $oldContext = SolutionFor::SetContext();  # use a context in which equality is defined
   62   my $f = main::Formula(shift);                # get equation as a formula
   63   SolutionFor::RestoreContext($oldContext);    # go back to user's context
   64 
   65   #
   66   #  Get the professor's correct point
   67   #
   68   my $p = shift; $p = main::Point($p) if ref($p) eq "ARRAY";
   69   $p = main::Compute($p) unless Value::isValue($p);
   70 
   71   #
   72   #  Get any user options (e.g., vars => ['x','y'])
   73   #
   74   my %options = (vars=>undef,@_);
   75 
   76   #
   77   #  Do some error checking
   78   #
   79   Value::Error("Your formula doesn't look like an implicit equation")
   80      unless $f->type eq 'Equality';
   81   Value::Error("Professor's answer should be a point or a number")
   82      unless Value::isNumber($p) || $p->type eq 'Point';
   83 
   84   #
   85   #  Save the formula for future reference, and make a callable
   86   #  perl function out of it.
   87   #
   88   $p->{f} = $f;
   89   $p->{F} = $f->perlFunction(undef,$options{vars});
   90 
   91   #
   92   #  Save some data about the original object
   93   #  and make the Value package think we are one of its objects
   94   #
   95   $p->{originalClass} = $p->cmp_class;
   96   $p->{isValue} = 1;
   97 
   98   #
   99   #  Make the object into the correct SolutionFor subclass
  100   #
  101   $p = bless $p, "SolutionFor::".$p->class;
  102 
  103   #
  104   #  Make sure professor's answer actually works
  105   #
  106   Value::Error("Professor's answer of ".$p->string." does not satisfy the given equation")
  107     unless $p->f($p);
  108 
  109   #
  110   #  Return the SolutionFor object
  111   #
  112   return $p;
  113 }
  114 
  115 ######################################################################
  116 #
  117 #  Define the new class (we make subclasses below)
  118 #  (we need subclasses in order to make things work
  119 #  properly with single-variable complex or real-valued
  120 #  equations)
  121 #
  122 package SolutionFor;
  123 
  124 #
  125 #  Evaluate the formula on the given point
  126 #
  127 sub f {
  128   my $self = shift;
  129   &{$self->{F}}(shift->value);
  130 }
  131 
  132 #
  133 #  The name of this object for error messages
  134 #
  135 sub cmp_class {shift->{originalClass}}
  136 
  137 #
  138 #  Do a comparison by testing if the formula's equality
  139 #  operation returns true or false.
  140 #  (Since we are implementing <=> here, we need
  141 #  to return 0 when true and 1 when false.)
  142 #
  143 sub compare {
  144   my ($l,$r,$flag) = @_;
  145   if ($l->promotePrecedence($r)) {return $r->compare($l,!$flag)}
  146   $r = Value::makeValue($r);
  147   return ($l->f($r)) ? 0 : 1;
  148 }
  149 
  150 #
  151 #  Set up a new context that is a copy of the current one, but
  152 #  has the equality operator defined, and the SolutionFor object
  153 #  prededence set so that comparisons with points or numbers will
  154 #  be promoted to comparisons with the SolutionFor
  155 #
  156 sub SetContext {
  157   my $oldContext = main::Context();
  158   $oldContext->{precedence}{SolutionFor} = $oldContext->{precedence}{special};
  159   my $context = $oldContext->copy;
  160   Parser::BOP::equality->Allow($context);
  161   main::Context($context);
  162   return $oldContext
  163 }
  164 
  165 #
  166 #  Put back a saved context
  167 #
  168 sub RestoreContext {main::Context(@_)}
  169 
  170 
  171 ######################################################################
  172 #
  173 #  A separate class for Reals, to get Value::Real in the ISA list
  174 #
  175 package SolutionFor::Real;
  176 our @ISA = qw(SolutionFor Value::Real Value);
  177 
  178 #
  179 #  Pass the real number directly
  180 #
  181 sub f {
  182   my $self = shift;
  183   &{$self->{F}}(shift);
  184 }
  185 
  186 
  187 ######################################################################
  188 #
  189 #  A separate class for Complexes
  190 #
  191 package SolutionFor::Complex;
  192 our @ISA = qw(SolutionFor Value::Complex Value);
  193 
  194 #
  195 #  Pass the complex number directly
  196 #
  197 sub f {
  198   my $self = shift;
  199   &{$self->{F}}(shift);
  200 }
  201 
  202 ######################################################################
  203 #
  204 #  A separate class for Points
  205 #
  206 package SolutionFor::Point;
  207 our @ISA = qw(SolutionFor Value::Point Value);
  208 
  209 #
  210 #  Use the Point's defaults, but turn off coordinate hints
  211 #  (since a wrong coordinate isn't detectable)
  212 #
  213 sub cmp_defaults {(
  214   shift->SUPER::cmp_defaults,
  215   showCoordinateHints => 0,
  216 )}
  217 
  218 1; # make Perl happy

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9