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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5393 - (download) (as text) (annotate)
Mon Aug 20 04:23:02 2007 UTC (12 years, 5 months ago) by dpvc
File size: 8337 byte(s)
Implements a general antiderivative formula object where the student
must include a "+ C" (using any variable he or she chooses) and the
correct answer also shows a "+ C".  See the comments in the file for
details.

    1 loadMacros("MathObjects.pl");
    2 
    3 sub _parserFormulaUpToConstant_init {FormulaUpToConstant::Init()}
    4 
    5 =head1 FormulaUpToConstant();
    6 
    7  ######################################################################
    8  #
    9  #  This file implements the FormulaUpToConstant object, which is
   10  #  a formula that is only unique up to a constant (i.e., this is
   11  #  an anti-derivative). Students must include the "+C" as part of
   12  #  their answers, but they can use any (single-letter) constant that
   13  #  they want, and it doesn't have to be the one the professor used.
   14  #
   15  #  To use FormulaWithConstat objects, load this macro file at the
   16  #  top of your problem:
   17  #
   18  #    loadMacros("parserFormulaUpToConstant");
   19  #
   20  #  then create a formula with constant as follows:
   21  #
   22  #    $f = FormulaUpToConstant("sin(x)+C");
   23  #
   24  #  Note that the C should NOT already be a variable in the Context;
   25  #  the FormulaUpToConstant object will handle adding it in for
   26  #  you.  If you don't include a constant in your formula (i.e., if
   27  #  all the variables that you used are already in your Context,
   28  #  then the FormulaUpToConstant object will add "+C" for you.
   29  #
   30  #  The FormulaUpToConstant should work like any normal Formula,
   31  #  and in particular, you use $f->cmp to get its answer checker.
   32  #
   33  #    ANS($f->cmp);
   34  #
   35  #  Note that the FormulaUpToConstant object creates its only private
   36  #  copy of the current Context (so that it can add variables without
   37  #  affecting the rest of the problem).  You should not notice this
   38  #  in general, but if you need to access that context, use $f->{context}.
   39  #  E.g.
   40  #
   41  #    Context($f->{context});
   42  #
   43  #  would make the current context the one being used by the
   44  #  FormulaUpToConstant, while
   45  #
   46  #    $f->{context}->variables->names
   47  #
   48  #  would return a list of the variables in the private context.
   49  #
   50  #  To get the name of the constant in use in the formula,
   51  #  use
   52  #
   53  #    $f->constant.
   54  #
   55  #  If you combine a FormulaUpToConstant with other formulas,
   56  #  the result will be a new FormulaUpToConstant object, with
   57  #  a new Context, and potentially a new + C added to it.  This
   58  #  is likely not what you want.  Instead, you should convert
   59  #  back to a Formula first, then combine with other objects,
   60  #  then convert back to a FormulaUpToConstant, if necessary.
   61  #  To do this, use the removeConstant() method:
   62  #
   63  #    $f = FormulaUpToConstant("sin(x)+C");
   64  #    $g = Formula("cos(x)");
   65  #    $h = $f->removeConstant + $g;  # $h will be "sin(x)+cos(x)"
   66  #    $h = FormulaUpToConstant($h);  # $h will be "sin(x)+cos(x)+C"
   67  #
   68  ######################################################################
   69 
   70 =cut
   71 
   72 package FormulaUpToConstant;
   73 @ISA = ('Value::Formula');
   74 
   75 sub Init {
   76   main::PG_restricted_eval('sub FormulaUpToConstant {FormulaUpToConstant->new(@_)}');
   77 }
   78 
   79 #
   80 #  Create an instance of a FormulaUpToConstant.  If no constant
   81 #  is supplied, we add C ourselves.
   82 #
   83 sub new {
   84   my $self = shift; my $class = ref($self) || $self;
   85   #
   86   #  Copy the context (so we can modify it) and
   87   #  replace the usual Variable object with our own.
   88   #
   89   my $context = (Value::isContext($_[0]) ? shift : $self->context)->copy;
   90   $context->{parser}{Variable} = 'FormulaUpToConstant::Variable';
   91   #
   92   #  Create a formula from the user's input.
   93   #
   94   my $f = main::Formula($context,@_);
   95   #
   96   #  If it doesn't have a constant already, add one.
   97   #  (should check that C isn't already in use, and look
   98   #   up the first free name, but we'll cross our fingers
   99   #   for now.  Could look through the defined variables
  100   #   to see if there is already an arbitraryConstant
  101   #   and use that.)
  102   #
  103   unless ($f->{constant}) {$f = $f + "C", $f->{constant} = "C"}
  104   #
  105   #  Check that the formula is linear in C.
  106   #
  107   my $n = $f->D($f->{constant});
  108   Value->Error("Your formula isn't linear in the arbitrary constant '%s'",$f->{constant})
  109     unless $n->isConstant;
  110   #
  111   #  Make a version with an adaptive parameter for use in the
  112   #  comparison later on.  We could like n0*C, but already have $n
  113   #  copies of C, so remove them.  That way, n0 will be 0 when there
  114   #  are no C's in the student answer during the adaptive comparison.
  115   #  (Again, should really check that n0 is not in use already)
  116   #
  117   my $n0 = $context->variables->get("n0");
  118   $context->variables->add(n0=>'Parameter') unless $n0 and $n0->{parameter};
  119   $f->{adapt} = $f + "(n0-$n)$f->{constant}";
  120   return bless $f, $class;
  121 }
  122 
  123 ##################################################
  124 #
  125 #  Remember that compare implements the overloaded perl <=> operator,
  126 #  and $a <=> $b is -1 when $a < $b, 0 when $a == $b and 1 when $a > $b.
  127 #  In our case, we only care about equality, so we will return 0 when
  128 #  equal and other numbers to indicate the reason they are not equal
  129 #  (this can be used by the answer checker to print helpful messages)
  130 #
  131 sub compare {
  132   my ($l,$r) = @_; my $self = $l; my $context = $self->context;
  133   $r = Value::makeValue($r,context=>$context);
  134   #
  135   #  Not equal if the student value is constant or has no + C
  136   #
  137   return 2 if !Value::isFormula($r);
  138   return 3 if !defined($r->{constant});
  139   #
  140   #  If constants aren't the same, substitute the professor's in the student answer.
  141   #
  142   $r = $r->substitute($r->{constant}=>$l->{constant}) unless $r->{constant} eq $l->{constant};
  143   #
  144   #  Compare with adaptive parameters to see if $l + n0 C = $r for some n0.
  145   #
  146   return -1 unless $l->{adapt} == $r;
  147   #
  148   #  Check that n0 is non-zero (i.e., there is a multiple of C in the student answer)
  149   #  (remember: return value of 0 is equal, and non-zero is unequal)
  150   #
  151   return abs($context->variables->get("n0")->{value}) < $context->flag("zeroLevelTol");
  152 }
  153 
  154 #
  155 #  Show hints by default
  156 #
  157 sub cmp_defaults {((shift)->SUPER::cmp_defaults,showHints => 1)};
  158 
  159 #
  160 #  Add useful messages, if the author requested them
  161 #
  162 sub cmp_postprocess {
  163   my $self = shift; my $ans = shift;
  164   $self->SUPER::cmp_postprocess($ans);
  165   return unless $ans->{score} == 0 && !$ans->{isPreview};
  166   return if $ans->{ans_message} || !$self->getFlag("showHints");
  167   my $result = $ans->{correct_value} <=> $ans->{student_value};  # compare encodes the reason in the result
  168   $self->cmp_Error($ans,"Note: there is always more than one posibility") if $result == 2 || $result == 3;
  169   $self->cmp_Error($ans,"Your answer is not the most general solution")
  170     if $result == 1 || ($result == 3 && $self->removeConstant == $ans->{student_value});
  171 }
  172 
  173 #
  174 #  Get the name of the constant
  175 #
  176 sub constant {(shift)->{constant}}
  177 
  178 #
  179 #  Remove the constant and return a Formula object
  180 #
  181 sub removeConstant {
  182   my $self = shift;
  183   main::Formula($self->substitute($self->{constant}=>0))->reduce;
  184 }
  185 
  186 #
  187 #  Override the differentiation so that we always return
  188 #  a Formula, not a FormulaUpToConstant (we don't want to
  189 #  add the C in again).
  190 #
  191 sub D {
  192   my $self = shift;
  193   $self->removeConstant->D(@_);
  194 }
  195 
  196 ######################################################################
  197 #
  198 #  This class repalces the Parser::Variable class, and its job
  199 #  is to look for new constants that aren't in the context,
  200 #  and add them in.  This allows students to use ANY constant
  201 #  they want, and a different one from the professor.  We check
  202 #  that the student only used ONE arbitrary constant, however.
  203 #
  204 package FormulaUpToConstant::Variable;
  205 our @ISA = ('Parser::Variable');
  206 
  207 sub new {
  208   my $self = shift; my $class = ref($self) || $self;
  209   my $equation = shift; my $variables = $equation->{context}{variables};
  210   my ($name,$ref) = @_; my $def = $variables->{$name};
  211   #
  212   #  If the variable is not already in the context, add it
  213   #    and mark it as an arbitrary constant (for later reference)
  214   #
  215   if (!defined($def) && length($name) eq 1) {
  216     $equation->{context}->variables->add($name => 'Real');
  217     $equation->{context}->variables->set($name => {arbitraryConstant => 1});
  218     $def = $variables->{$name};
  219   }
  220   #
  221   #  If the variable is an arbitrary constant
  222   #    Error if we already have a constant and it's not this one.
  223   #    Save the constant so we can check with it later.
  224   #
  225   if ($def && $def->{arbitraryConstant}) {
  226     $equation->Error(["Your formula shouldn't have two arbitrary constants"],$ref)
  227       if $equation->{constant} and $name ne $equation->{constant};
  228     $equation->{constant} = $name;
  229   }
  230   #
  231   #  Do the usual Variable stuff.
  232   #
  233   $self->SUPER::new($equation,$name,$ref);
  234 }
  235 
  236 
  237 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9