How can I parse a mathematical function from user input in Perl?
I wrote a tool for polar functions. It lists values from an input range like that:
0 Grad: (0 RAD|1 RES) 20 Grad: (0.349065850398866 RAD|1.3639702342662 RES) 40 Grad: (0.698131700797732 RAD|1.83909963117728 RES) 60 Grad: (1.0471975511966 RAD|2.73205080756888 RES) 80 Grad: (1.39626340159546 RAD|6.67128181961771 RES) 100 Grad: (1.74532925199433 RAD|4.67128181961771 RES) 120 Grad: (2.0943951023932 RAD|0.732050807568878 RES) 140 Grad: (2.44346095279206 RAD|0.16090036882272 RES) 160 Grad: (2.79252680319093 RAD|0.636029765733797 RES) 180 Grad: (3.14159265358979 RAD|1 RES)
It is based of the function
abs(1 + tan($_[0]));
How can I parse such a function from UserInput (Perl syntax) and assign it to a variable?
I want to avoid changing the Perl script; making the function dynamic instead of static.
Greets and thanks for reading.
EDIT: sorry for quadrupelpost....
Thanks for the help, but the following snippet gives wrong values:
print("Eingabe: Funktion (phi = $t); PERL syntax!: > ");
$iFunktion = <STDIN>;
chop($iFunktion);
print("Eingabe: Grad Start: > ");
$iGradStart = <STDIN>;
chop($iGradStart);
print("Eingabe: Grad End: > ");
$iGradEnd = <STDIN>;
chop($iGradEnd);
print("Eingabe: Schrittweite: > ");
$iSchrittweite = <STDIN>;
chop($iSchrittweite);
print("\nBerechne Funktion von $iGradStart bis $iGradEnd Grad mit einer Schrittweite von $iSchrittweite\n");
for ($i = $iGradStart; $i < $iGradEnd; $i = $i + $iSchrittweite)
{
$flRad = °2rad($i);
#$flResult = &Compute($flRad);
$t = $i;
$flResult = eval($iFunktion);
print("$i Grad: ($flRad RAD|$flResult RES) \n");
}
Input was abs(1 + tan($t));
(additional info, merged from follow-up)
print("Eingabe: Grad Start: > ");
$iGradStart = <STDIN>;
chop($iGradStart); 开发者_运维知识库
print("Eingabe: Grad End: > ");
$iGradEnd = <STDIN>;
chop($iGradEnd);
print("Eingabe: Schrittweite: > ");
$iSchrittweite = <STDIN>; chop($iSchrittweite);
print("\nfrom $iGradStart to $iGradEnd Grad with $iSchrittweite\n");
for ($i = $iGradStart; $i <= $iGradEnd; $i = $i + $iSchrittweite)
{
$flRad = °2rad($i);
$flResult = &Compute($flRad);
print("$i Grad: ($flRad RAD|$flResult RES) \n");
}
sub Compute { return abs(1 + tan($_[0])); }
You should look at the eval
statement. It allows you to evaluate a string as Perl code.
For example, this code:
print "Function? ";
chomp($function = <STDIN>);
for ($i = 0;$i < 10;$i++) {
print $i,"->",eval($function),"\n";
}
Gives this:
Function? $i * $i
0->0
1->1
2->4
3->9
4->16
5->25
6->36
7->49
8->64
9->81
As you're running user input as code, if someone other than use is using your script, you'll want to do something to sanitise user input. You may also want to do a substitution so users could, for example, enter x
instead of $i
etc. If you want to catch errors from eval
check the $@
variable.
Depending on what else you have going on, I think I'd skip the parsing idea and have the users create a Perl library or subclass. They wrap their code in subroutine names and tell your script the library and the sub name. You might also combine that with a proper config file or command-line options.
Math::Expression::Evaluator almost does what you want, except that has no abs()
and no way to add user functions (callbacks.) It took me about five minutes to add some rudimentary support, thought.
#!/usr/bin/perl
use strict;
use warnings;
use Math::Trig;
use Math::Expression::Evaluator;
my $expr = join ' ', @ARGV;
die "No expression provided" unless $expr;
my $m = Math::Expression::Evaluator->new;
# NOT in the distribution
$m->add_user_function('abs', sub { abs($_[0]) });
my $func = $m->parse($expr);
die "Cannot parse" unless $func;
for my $i (0 .. 10){
my $rads = deg2rad($i);
my $res = $func->val({x => $_});
print("$i Grad: ($rads RAD|$res RES) \n");
}
精彩评论