2011-10-13 112 views
1
#!/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. 

有關$運變量未初始化。我想這可能是一個範圍問題......但我無法弄清楚。我試過了我能想到的所有東西(初始化循環外部的變量等),但是在運行該程序時似乎沒有任何區別。任何建議將不勝感激!

+2

一百多線條沒有'strict'?原型?代碼應該做什麼? –

+0

這只是C++代碼的一個快速轉換,用於評估表達式字符串(例如「1 + 2 * 3)」。 –

+8

在每個子例程聲明中刪除名稱後面的「()」,您一直說這個單詞但我確實不要認爲這意味着你在想什麼,在代碼頂部放置'use strict;',修復你得到的每一個錯誤,重新發布,在做前者時,重複'Perl不是C++'一遍又一遍。 –

回答

5

你只使用包(〜全局)變量,這是因爲一個巨大的問題你正在使用遞歸函數!首先,添加

use strict; 

首先,這將識別您未聲明的變量。使用my將它們聲明在適當的範圍內。


你試圖將數組傳遞給潛艇,但你失敗了。唯一可以傳遞給子的是標量列表。如果你想傳遞一個數組到一個子,你需要傳遞一個引用(〜指針)到數組。

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

foo(\@expressions, $index); 

這就是您收到警告的原因。您將一個元素分配給一個數組(@expression = $_[0]),然後嘗試索引第二個或更新的元素。


通過使用原型(),您告訴Perl子無任何參數。然後你使用&告訴Perl忽略原型,以便你可以傳遞參數給你的潛艇。在子呼叫之前擺脫子名稱後的()&


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

可以降低到

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

Higher Order Perl有一個chapter on parsing。關於如何從頭開始構建表達式分析器和評估器,請參見第8.1.2節。

您還可以查看Parse::RecDescent提供的demo calculator script

出於好奇,我想看看不使用解析器可以實現什麼。下面的腳本做了很多假設,但對於簡單的情況是「有效的」。

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

帶有一點推論思南的答案,這裏是一個「解析器」從駱駝的另一邊寫。

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