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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2731 - (download) (as text) (annotate)
Sat Sep 4 20:40:43 2004 UTC (15 years, 5 months ago) by dpvc
File size: 3647 byte(s)
This file defines a NumberWithUnits class for that provides an answer
checker for numbers with units.  It uses the same units package that
the standard answer checkers use, but this will eventually be replaced
by a more complete Units class within the parser itself.  The class
produces a preview string in which the units show appropriately.

    1 loadMacros('Parser.pl');
    2 
    3 sub _parserNumberWithUnits_init {}; # don't reload this file
    4 
    5 ######################################################################
    6 #
    7 #  This is a Parser class that implements a number with units.
    8 #  It is a temporary version until the Parser can handle it
    9 #  directly.
   10 #
   11 #  Use NumberWithUnits("num units") or NumberWithUnits(formula,"units")
   12 #  to generate a NumberWithUnits object, and then call its cmp method
   13 #  to get an answer checker for your number with units.
   14 #
   15 #  Usage examples:
   16 #
   17 #      ANS(NumberWithUnits("3 ft")->cmp);
   18 #      ANS(NumberWithUnits("$a*$b ft")->cmp);
   19 #      ANS(NumberWithUnits($a*$b,"ft")->cmp);
   20 #
   21 
   22 sub NumberWithUnits {NumberWithUnits->new(@_)}
   23 
   24 #
   25 #  Define a new Value object for NumberWithUnits
   26 #
   27 package NumberWithUnits;
   28 our @ISA = qw(Value::Real);
   29 
   30 sub new {
   31   my $self = shift; my $class = ref($self) || $self;
   32   my $num = shift; my $units = shift;
   33   Value::Error("You must provide a number") unless defined($num);
   34   ($num,$units) = $num =~ m/^(.*)\s+(\S*)$/ unless $units;
   35   Value::Error("You must provide units for your number")
   36     unless $units;
   37   $num = Value::makeValue($num);
   38   my %Units = getUnits($units);
   39   Value::Error($Units{ERROR}) if ($Units{ERROR});
   40   $num->{units} = $units;
   41   $num->{units_ref} = \%Units;
   42   $num->{isValue} = 1;
   43   bless $num, $class;
   44 }
   45 
   46 #
   47 #  Add the units to the string value
   48 #
   49 sub string {
   50   my $self = shift;
   51   $self->SUPER::string . " " . $self->{units};
   52 }
   53 
   54 #
   55 #  Add the units to the TeX value
   56 #
   57 sub TeX {
   58   my $self = shift;
   59   $self->SUPER::TeX . '\ ' . TeXunits($self->{units});
   60 }
   61 
   62 
   63 sub cmp_class {'a Number with Units'};
   64 
   65 #
   66 #  Replace the cmp_parse with one that removes the units
   67 #  from the student answer and checks them.  The answer
   68 #  value is adjusted by the factors, and then checked.
   69 #  Finally, the units themselves are checked.
   70 #
   71 sub cmp_parse {
   72   my $self = shift; my $ans = shift;
   73   #
   74   #  Check that the units are defined and legal
   75   #
   76   my ($num,$units) = $ans->{student_ans} =~ m/^(.*)\s+(\S*)$/;
   77   unless (defined($num) && $units) {
   78     $self->cmp_Error($ans,"Your answer doesn't look like a number with units");
   79     return $ans;
   80   }
   81   my %Units = getUnits($units);
   82   if ($Units{ERROR}) {$self->cmp_Error($ans,$Units{ERROR}); return $ans}
   83   #
   84   #  Check the numeric part of the answer
   85   #   and adjust the answer strings
   86   #
   87   $ans->{correct_value} *= $self->{units_ref}{factor}/$Units{factor};
   88   $ans->{student_ans} = $num;
   89   $ans = $self->SUPER::cmp_parse($ans);
   90   $ans->{student_ans} .= " " . $units;
   91   $ans->{preview_text_string}  .= " ".$units;
   92   $ans->{preview_latex_string} .= '\ '.TeXunits($units);
   93   #
   94   #  If there is not already a message, check the units
   95   #
   96   return $ans unless $ans->{ans_message} eq '';
   97   foreach my $funit (keys %{$self->{units_ref}}) {
   98     next if $funit eq 'factor';
   99     next if $self->{units_ref}{$funit} == $Units{$funit};
  100     $self->cmp_Error($ans,"The units for your answer are not correct")
  101       unless $ans->{isPreview};
  102     $ans->score(0); last;
  103   }
  104   return $ans;
  105 }
  106 
  107 #
  108 #  Convert units to TeX format
  109 #  (fix superscripts, put terms in \rm,
  110 #   and make a \frac out of fractions)
  111 #
  112 sub TeXunits {
  113   my $units = shift;
  114   $units =~ s/\^\(?([-+]?\d+)\)?/^{$1}/g;
  115   $units =~ s/\*/\\,/g;
  116   return '{\rm '.$units.'}' unless $units =~ m!^(.*)/(.*)$!;
  117   return '\frac{\rm\mathstrut '.$1.'}{\rm\mathstrut '.$2.'}';
  118 }
  119 
  120 #
  121 #  Get the units hash and fix up the errors
  122 #
  123 sub getUnits {
  124   my $units = shift;
  125   my %Units = Units::evaluate_units($units);
  126   if ($Units{ERROR}) {
  127     $Units{ERROR} =~ s/ at ([^ ]+) line \d+(\n|.)*//;
  128     $Units{ERROR} =~ s/^UNIT ERROR:? *//;
  129   }
  130   return %Units;
  131 }
  132 
  133 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9