开发者

Uninitialized variable issue in Perl program

#!/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";

My console returns these warnings:

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.

about the $op variable being uninitialized. I'm thinking this may be a scope problem...but I can't figure it out. I've tried everything I could think of (initializing the variable outside of the loop, etc.), but none of it seems to make a difference when running the program. Any suggestions would be greatly appreciated!


You're only using package (~global) variables, which is a huge problem given that you are using recursive functions! Start by adding

use strict;

Primarily, this will identify the variables you haven't declared. Use my to declare them in the appropriate scope.


You're trying to pass arrays to the subs, but you're failing. The only thing that can be passed to a sub is a list of scalars. If you want to pass an array to a sub, you'll need to pass a reference (~pointer) to the array.

sub foo {
   my ($expressions, $index) = @_;
   print($expressions->[$index], "\n");
}

foo(\@expressions, $index);

This is the reason you're getting the warnings. You are assigning one element to an array (@expression = $_[0]), then you try to index the second or later element.


By using prototype (), you're telling Perl the sub takes no arguments. Then you use & to tell Perl to ignore the prototype so you can pass arguments to your subs. Get rid of both the () after the sub names and & before sub calls.


my $more = 1;
while ($more) {
   ...
   if (cond) {
      ...
   } else {
      $more = 0;
   }
}

can be reduced to

while (1) {
   ...
   last if !cond;
   ...
}


Higher Order Perl has a chapter on parsing. See section 8.1.2 for how you would build an expression parser and evaluator from scratch.

You can also take a look at the demo calculator script provided with Parse::RecDescent.

Just out of curiosity, I wanted to see what can be achieved without using parsers. The following script makes a lot of assumptions, but "works" for the simple cases.

#!/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;
}

Output:

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

But, of course, it's fragile:

C:\Temp> z "2*3+2*5"
'2*3+2*5' evaluated to 610
Wrong result, should have been '16'


As a bit of a corollary to Sinan's answer, here is a "parser" written from the other side of the camel.

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";

Which when run:

$ perl eval.pl 2*3+2*5

prints:

6 = 2 * 3
10 = 2 * 5
16 = 6 + 10
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜