#!/usr/bin/perl
use warnings;
use Scalar::Util qw(looks_like_number);
sub term_value();
sub factor_value();
sub expression_value()
{
$num = @_;
@expression = $_[0];
print "expression[0]: " . $expression[0] . "\n";
$index = $_[$num-1];
print "index: $index\n";
$result = &term_value(@expression, $index);
$more = 1;
while($more)
{
$op = $expression[$index];
print "$op\n";
if ($op eq "+" || $op eq "-")
{
$index++;
$value = &term_value(@expression, $index);
if ($op eq '+')
{
$result = $result + $value;
} else {
$result = $result - $value;
}
}
else
{
$more = 0;
}
}
return $result;
}
sub term_value()
{
$num = @_;
@expression = $_[0];
print "expression[0]: " . $expression[0] . "\n";
$index = $_[$num-1];
print "index: $index\n";
$result = &factor_value(@expression, $index);
$more = 1;
while($more)
{
$op = $expression[$index];
if ($op eq "*" || $op eq "/")
{
$index++;
$value = &factor_value(@expression, $index);
if ($op eq '*')
{
$result = $result * $value;
} else {
$result = $result / $value;
}
} else {
$more = 0;
}
}
return $result;
}
sub factor_value()
{
$num = @_;
@expression = $_[0];
print "expression[0]: " . $expression[0] . "\n";
$index = $_[$num-1];
print "index: $index\n";
$result = 0;
$c = $expression[$index];
if ($c eq '(')
{
$index++;
$result = &expression_value(@expression, $index);
$index++;
} else {
while (looks_like_number($c))
{
$result = 10 * $result + $c - '0';
$index++;
$c = $expression[$index];
}
}
return $result;
}
#Collect argument and separate by character
@one_char = split(//, $ARGV[0]);
$index = 0;
$result = &expression_value(@one_char, $index);
print $result . "\n";
我的控制台会返回以下警告:
Use of uninitialized value $op in string eq at eval.pl line 58.
Use of uninitialized value $op in string eq at eval.pl line 58.
Use of uninitialized value $op in string eq at eval.pl line 25.
Use of uninitialized value $op in string eq at eval.pl line 25.
关于未初始化的$ op变量。我认为这可能是一个范围问题...但我无法弄明白。我已经尝试了我能想到的一切(在循环之外初始化变量等),但是在运行程序时似乎没有任何区别。任何建议将不胜感激!
答案 0 :(得分:5)
你只使用包(~global)变量,这是一个很大的问题,因为你正在使用递归函数!首先添加
use strict;
首先,这将识别您尚未声明的变量。使用my
在适当的范围内声明它们。
你试图将数组传递给潜艇,但你失败了。唯一可以传递给sub的是标量列表。如果要将数组传递给sub,则需要将引用(〜指针)传递给数组。
sub foo {
my ($expressions, $index) = @_;
print($expressions->[$index], "\n");
}
foo(\@expressions, $index);
这就是你收到警告的原因。您正在为数组(@expression = $_[0]
)分配一个元素,然后尝试索引第二个或更晚的元素。
通过使用原型()
,你告诉Perl sub不带参数。然后使用&
告诉Perl忽略原型,这样你就可以将参数传递给你的潜艇了。在子调用之前删除()
和在子调用之前删除&
。
my $more = 1;
while ($more) {
...
if (cond) {
...
} else {
$more = 0;
}
}
可以缩减为
while (1) {
...
last if !cond;
...
}
答案 1 :(得分:2)
Higher Order Perl有一个chapter on parsing。有关如何从头开始构建表达式解析器和求值程序的信息,请参见第8.1.2节。
您还可以查看demo calculator script提供的Parse::RecDescent。
出于好奇,我想看看如果不使用解析器可以实现什么。以下脚本做了很多假设,但对于简单的情况“有效”。
#!/usr/bin/env perl
use strict;
use warnings;
use Regexp::Common qw(balanced number);
die "Need expression\n" unless @ARGV;
my ($expression) = @ARGV;
my $result = evaluate_expression($expression);
printf(
"'%s' evaluated to %g\n",
$expression, $result
);
my $expected = eval $expression;
unless ($result == $expected) {
die "Wrong result, should have been '$expected'\n";
}
sub evaluate_expression {
my ($expression) = @_;
my $n = qr!$RE{num}{real}!;
my $mul = qr![*/]!;
my $add = qr![+-]!;
my $subexpr = qr!$RE{balanced}{-parens=>'()'}{-keep}!;
1 while
$expression =~ s!
$subexpr
!
my $s = $1;
$s =~ s{(?:^\()|(?:\)\z)}{}g;
evaluate_expression($s)
!gex;
1 while
$expression =~ s!($n) \s* ($mul) \s* ($n)!"$1 $2 $3"!geex;
1 while
$expression =~ s!($n) \s* ($add) \s* ($n)!"$1 $2 $3"!geex;
return $expression;
}
输出:
C:\Temp> z "((1+1)*3 +2)*5" '((1+1)*3 +2)*5' evaluated to 40 C:\Temp> z "(1+1)*3 + 2*5" '(1+1)*3 + 2*5' evaluated to 16
但是,当然,它很脆弱:
C:\Temp> z "2*3+2*5" '2*3+2*5' evaluated to 610 Wrong result, should have been '16'
答案 2 :(得分:0)
作为思南答案的必然结果,这里有一个从骆驼另一边写的“解析器”。
use 5.010;
use strict;
use warnings;
my @ops;
use overload map {
my $op = $_;
$op => sub {
my ($x, $y) = @_[$_[2] ? (1, 0) : (0, 1)];
bless [$x, $op, $y]
}
} @ops = qw(+ - / *);
my %ops = map {$_ => eval "sub {\$_[0] $_ \$_[1]}"} @ops;
sub eval {
my $self = shift;
return $$self[0] if @$self == 1;
my ($x, $op, $y) = map {ref eq 'main' ? $_->eval : $_} @$self;
my $ret = $ops{$op}->($x, $y);
say "$ret = $x $op $y";
$ret;
}
BEGIN {overload::constant integer => sub {bless [$_[1]]}}
eval->eval for "@ARGV";
运行时:
$ perl eval.pl 2*3+2*5
打印:
6 = 2 * 3
10 = 2 * 5
16 = 6 + 10