[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 5395 - (download) (as text) (annotate)
Mon Aug 20 04:43:48 2007 UTC (12 years, 5 months ago) by dpvc
File size: 8579 byte(s)
Fixed typo in documentation.

    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.pl");
   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  #  The answer evaluator by default will give "helpful" messages
   69  #  to the student when the "+ C" is left out.  You can turn off
   70  #  these messages using the showHints option to the cmp() method:
   71  #
   72  #    ANS($f->cmp(showHints => 0));
   73  #
   74  ######################################################################
   75 
   76 =cut
   77 
   78 package FormulaUpToConstant;
   79 @ISA = ('Value::Formula');
   80 
   81 sub Init {
   82   main::PG_restricted_eval('sub FormulaUpToConstant {FormulaUpToConstant->new(@_)}');
   83 }
   84 
   85 #
   86 #  Create an instance of a FormulaUpToConstant.  If no constant
   87 #  is supplied, we add C ourselves.
   88 #
   89 sub new {
   90   my $self = shift; my $class = ref($self) || $self;
   91   #
   92   #  Copy the context (so we can modify it) and
   93   #  replace the usual Variable object with our own.
   94   #
   95   my $context = (Value::isContext($_[0]) ? shift : $self->context)->copy;
   96   $context->{parser}{Variable} = 'FormulaUpToConstant::Variable';
   97   #
   98   #  Create a formula from the user's input.
   99   #
  100   my $f = main::Formula($context,@_);
  101   #
  102   #  If it doesn't have a constant already, add one.
  103   #  (should check that C isn't already in use, and look
  104   #   up the first free name, but we'll cross our fingers
  105   #   for now.  Could look through the defined variables
  106   #   to see if there is already an arbitraryConstant
  107   #   and use that.)
  108   #
  109   unless ($f->{constant}) {$f = $f + "C", $f->{constant} = "C"}
  110   #
  111   #  Check that the formula is linear in C.
  112   #
  113   my $n = $f->D($f->{constant});
  114   Value->Error("Your formula isn't linear in the arbitrary constant '%s'",$f->{constant})
  115     unless $n->isConstant;
  116   #
  117   #  Make a version with an adaptive parameter for use in the
  118   #  comparison later on.  We could like n0*C, but already have $n
  119   #  copies of C, so remove them.  That way, n0 will be 0 when there
  120   #  are no C's in the student answer during the adaptive comparison.
  121   #  (Again, should really check that n0 is not in use already)
  122   #
  123   my $n0 = $context->variables->get("n0");
  124   $context->variables->add(n0=>'Parameter') unless $n0 and $n0->{parameter};
  125   $f->{adapt} = $f + "(n0-$n)$f->{constant}";
  126   return bless $f, $class;
  127 }
  128 
  129 ##################################################
  130 #
  131 #  Remember that compare implements the overloaded perl <=> operator,
  132 #  and $a <=> $b is -1 when $a < $b, 0 when $a == $b and 1 when $a > $b.
  133 #  In our case, we only care about equality, so we will return 0 when
  134 #  equal and other numbers to indicate the reason they are not equal
  135 #  (this can be used by the answer checker to print helpful messages)
  136 #
  137 sub compare {
  138   my ($l,$r) = @_; my $self = $l; my $context = $self->context;
  139   $r = Value::makeValue($r,context=>$context);
  140   #
  141   #  Not equal if the student value is constant or has no + C
  142   #
  143   return 2 if !Value::isFormula($r);
  144   return 3 if !defined($r->{constant});
  145   #
  146   #  If constants aren't the same, substitute the professor's in the student answer.
  147   #
  148   $r = $r->substitute($r->{constant}=>$l->{constant}) unless $r->{constant} eq $l->{constant};
  149   #
  150   #  Compare with adaptive parameters to see if $l + n0 C = $r for some n0.
  151   #
  152   return -1 unless $l->{adapt} == $r;
  153   #
  154   #  Check that n0 is non-zero (i.e., there is a multiple of C in the student answer)
  155   #  (remember: return value of 0 is equal, and non-zero is unequal)
  156   #
  157   return abs($context->variables->get("n0")->{value}) < $context->flag("zeroLevelTol");
  158 }
  159 
  160 #
  161 #  Show hints by default
  162 #
  163 sub cmp_defaults {((shift)->SUPER::cmp_defaults,showHints => 1)};
  164 
  165 #
  166 #  Add useful messages, if the author requested them
  167 #
  168 sub cmp_postprocess {
  169   my $self = shift; my $ans = shift;
  170   $self->SUPER::cmp_postprocess($ans);
  171   return unless $ans->{score} == 0 && !$ans->{isPreview};
  172   return if $ans->{ans_message} || !$self->getFlag("showHints");
  173   my $result = $ans->{correct_value} <=> $ans->{student_value};  # compare encodes the reason in the result
  174   $self->cmp_Error($ans,"Note: there is always more than one posibility") if $result == 2 || $result == 3;
  175   $self->cmp_Error($ans,"Your answer is not the most general solution")
  176     if $result == 1 || ($result == 3 && $self->removeConstant == $ans->{student_value});
  177 }
  178 
  179 #
  180 #  Get the name of the constant
  181 #
  182 sub constant {(shift)->{constant}}
  183 
  184 #
  185 #  Remove the constant and return a Formula object
  186 #
  187 sub removeConstant {
  188   my $self = shift;
  189   main::Formula($self->substitute($self->{constant}=>0))->reduce;
  190 }
  191 
  192 #
  193 #  Override the differentiation so that we always return
  194 #  a Formula, not a FormulaUpToConstant (we don't want to
  195 #  add the C in again).
  196 #
  197 sub D {
  198   my $self = shift;
  199   $self->removeConstant->D(@_);
  200 }
  201 
  202 ######################################################################
  203 #
  204 #  This class repalces the Parser::Variable class, and its job
  205 #  is to look for new constants that aren't in the context,
  206 #  and add them in.  This allows students to use ANY constant
  207 #  they want, and a different one from the professor.  We check
  208 #  that the student only used ONE arbitrary constant, however.
  209 #
  210 package FormulaUpToConstant::Variable;
  211 our @ISA = ('Parser::Variable');
  212 
  213 sub new {
  214   my $self = shift; my $class = ref($self) || $self;
  215   my $equation = shift; my $variables = $equation->{context}{variables};
  216   my ($name,$ref) = @_; my $def = $variables->{$name};
  217   #
  218   #  If the variable is not already in the context, add it
  219   #    and mark it as an arbitrary constant (for later reference)
  220   #
  221   if (!defined($def) && length($name) eq 1) {
  222     $equation->{context}->variables->add($name => 'Real');
  223     $equation->{context}->variables->set($name => {arbitraryConstant => 1});
  224     $def = $variables->{$name};
  225   }
  226   #
  227   #  If the variable is an arbitrary constant
  228   #    Error if we already have a constant and it's not this one.
  229   #    Save the constant so we can check with it later.
  230   #
  231   if ($def && $def->{arbitraryConstant}) {
  232     $equation->Error(["Your formula shouldn't have two arbitrary constants"],$ref)
  233       if $equation->{constant} and $name ne $equation->{constant};
  234     $equation->{constant} = $name;
  235   }
  236   #
  237   #  Do the usual Variable stuff.
  238   #
  239   $self->SUPER::new($equation,$name,$ref);
  240 }
  241 
  242 
  243 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9