Ошибка неинициализированной переменной в программе Perl - PullRequest
1 голос
/ 13 октября 2011
#!/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 ]

5 голосов
/ 13 октября 2011

Вы используете только переменные пакета (~ global), что является огромной проблемой, учитывая, что вы используете рекурсивные функции!Начните с добавления

use strict;

Прежде всего, это определит переменные, которые вы не объявили.Используйте my, чтобы объявить их в соответствующей области.


Вы пытаетесь передать массивы в сабвуферы, но не можете.Единственное, что можно передать в подпрограмму, это список скаляров.Если вы хотите передать массив в подпрограмму, вам нужно передать ссылку (~ указатель) на массив.

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;
   ...
}
2 голосов
/ 13 октября 2011

Perl высшего порядка имеет главу о разборе .См. Раздел 8.1.2 о том, как вы могли бы создать анализатор и оценщик выражений с нуля.

Вы также можете взглянуть на скрипт демо-калькулятора , предоставляемый с 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'
0 голосов
/ 13 октября 2011

В качестве небольшого следствия для ответа Синан, вот «парсер», написанный с другой стороны верблюда.

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...