Perl程序中未初始化的变量问题

时间:2011-10-13 03:17:53

标签: perl scope

#!/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变量。我认为这可能是一个范围问题...但我无法弄明白。我已经尝试了我能想到的一切(在循环之外初始化变量等),但是在运行程序时似乎没有任何区别。任何建议将不胜感激!

3 个答案:

答案 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