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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3501 - (download) (as text) (annotate)
Sat Aug 13 01:40:45 2005 UTC (14 years, 5 months ago) by dpvc
File size: 4477 byte(s)
Added differentiation of (single-variable) functions created with
parserFunction.

    1 loadMacros('Parser.pl');
    2 
    3 sub _parserFunction_init {}; # don't reload this file
    4 
    5 ####################################################################
    6 #
    7 #  This file implements an easy way of creating new functions that
    8 #  are added to the current Parser context.  (This avoids having to
    9 #  do the complicated procedure outlined in the docs/parser/extensions
   10 #  samples.
   11 #
   12 #  To create a function that can be used in Formula() calls (and by
   13 #  students in their answers), use the parserFunction() routine, as
   14 #  in the following examples:
   15 #
   16 #     parserFunction(f => "sqrt(x+1)-2");
   17 #
   18 #     $x = Formula('x');
   19 #     parserFunction(f => sqrt($x+1)-2);
   20 #
   21 #     parserFunction("f(x)" => "sqrt(x+1)-2");
   22 #
   23 #     parserFunction("f(x,y)" => "sqrt(x*y)");
   24 #
   25 #  The first parameter to parserFunction is the name of the function
   26 #  or the name with its argument list.  In the first case, the
   27 #  names of the variables are taken from the formula for the
   28 #  function, and are listed in alphabetical order.
   29 #
   30 #  The second argument is the formula used to compute the value
   31 #  of the function.  It can be either a string or a Parser Formula
   32 #  object.
   33 #
   34 
   35 sub parserFunction {parserFunction->Create(@_)}
   36 
   37 #
   38 #  The package that will manage user-defined functions
   39 #
   40 package parserFunction;
   41 our @ISA = qw(Parser::Function);
   42 
   43 sub Create {
   44   my $self = shift; my $name = shift; my $formula = shift;
   45   my $context = $$Value::context;
   46   my @argNames; my @argTypes; my @newVars;
   47   #
   48   #  Look for argument names for the function
   49   #   (check that the arguments are ok, and temporarily
   50   #    add in any variables that are not already there)
   51   #
   52   if ($name =~ m/^([a-z0-9]+)\(\s*(.*?)\s*\)$/i) {
   53     $name = $1; my $args = $2;
   54     @argNames = split(/\s*,\s*/,$args);
   55     foreach my $x (@argNames) {
   56       Value::Error("Illegal variable name '%s'",$x) if $x =~ m/[^a-z]/i;
   57       unless ($context->{variables}{$x}) {
   58   $context->variables->add($x=>'Real');
   59   push(@newVars,$x);
   60       }
   61     }
   62   } else {
   63     Value::Error("Illegal function name '%s'",$name)
   64       if $name =~ m/[^a-z0-9]/i;
   65   }
   66   #
   67   #  Create the formula and get its arguments and types
   68   #
   69   $formula = Value::Formula->new($formula) unless Value::isFormula($formula);
   70   @argNames = main::lex_sort(keys(%{$formula->{variables}})) unless scalar(@argNames);
   71   foreach my $x (@argNames) {push(@argTypes,$context->{variables}{$x}{type})}
   72   #
   73   #  Add the function to the context and create the perl function
   74   #
   75   $context->functions->add(
   76     $name => {
   77       (length($name) == 1? (TeX=>$name): ()),
   78       @_, class => 'parserFunction', argCount => scalar(@argNames),
   79       argNames => [@argNames], argTypes => [@argTypes],
   80       function => $formula->perlFunction(undef,[@argNames]),
   81       formula => $formula, type => $formula->typeRef,
   82     }
   83   );
   84   main::PG_restricted_eval("sub main::$name {Parser::Function->call('$name',\@_)}");
   85   $context->variables->remove(@newVars) if scalar(@newVars);
   86 }
   87 
   88 #
   89 #  Check that there are the right number of arguments
   90 #  and they are of the right type.
   91 #
   92 sub _check {
   93   my $self = shift; my $name = $self->{name};
   94   return if $self->checkArgCount($self->{def}{argCount});
   95   my @argTypes = @{$self->{def}{argTypes}}; my $n = 0;
   96   foreach my $x (@{$self->{params}}) {
   97     my $atype = shift(@argTypes); $n++;
   98     $self->Error("The %s argument for '%s' should be of type %s",
   99      NameForNumber($n),$name,$atype->{name})
  100       unless (Parser::Item::typeMatch($atype,$x->{type}));
  101   }
  102   $self->{type} = $self->{def}{type};
  103 }
  104 
  105 #
  106 #  Call the function stored in the definition
  107 #
  108 sub _eval {
  109   my $self = shift; my $name = $self->{name};
  110   &{$self->{def}{function}}(@_);
  111 }
  112 
  113 #
  114 #  Check the arguments and compute the result.
  115 #
  116 sub _call {
  117   my $self = shift; my $name = shift;
  118   my $def = $$Value::context->{functions}{$name};
  119   &{$def->{function}}(@_);
  120 }
  121 
  122 #
  123 #  Compute the derivative of (single-variable) functions
  124 #    using the chain rule.
  125 #
  126 sub D {
  127   my $self = shift; my $def = $self->{def};
  128   $self->Error("Can't differentiate function '%s'",$self->{name})
  129     unless $def->{argCount} == 1;
  130   my $x = $def->{argNames}[0];
  131   my $Df = $def->{formula}->D($x);
  132   my $g = $self->{params}[0];
  133   return (($Df->substitute($x=>$g))*($g->D(@_)))->{tree}->reduce;
  134 }
  135 
  136 #
  137 #  Get the name for a number
  138 #
  139 sub NameForNumber {
  140   my $n = shift;
  141   my $name =  ('zeroth','first','second','third','fourth','fifth',
  142                'sixth','seventh','eighth','ninth','tenth')[$n];
  143   $name = "$n-th" if ($n > 10);
  144   return $name;
  145 }
  146 
  147 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9