使用perl创建一个简单的计算器

张剑桥*_*张剑桥 2 unix perl

我需要设计一个简单的计算器在Perl只有ex1.pl叫+-*,和/功能。以下是我所拥有的。

while (@ARGV>0) {
    if ($_=~m/(\d+)\s(.)\s(\d+)/) {
        if ($2 == "+") {
            print "$1 + $3\n";
        }   
        elsif ($2 == "-") {
            print "$1 - $3\n";
        }   
        elsif ($2 == "*") {
            print "$1 * $3\n";
        }   
        elsif ($2 == "/") {
            if ($3 == 0 ) { 
                print "$1 cannot be divided by 0\n";
            }   
            else {
                print "$1 / $3\n";
            }   
        }   
        else {
            print "operator not identified\n";
        }
    }   
    else {
        print "syntax error\n";
    }   
}
Run Code Online (Sandbox Code Playgroud)

例如,如果我输入./ex1.pl 5 + 2,它将继续报告错误

在第4行的模式匹配(m //)中使用未初始化的值

直到我按ctrl + c。谁能帮助我确定我做错了什么地方?

zdi*_*dim 5

循环while(@ARGV>0)不会分配给$_进行正则表达式匹配的对象,因此没有任何匹配,代码转到else。而且,@ARGV永远不会清空,因此代码处于无限循环中。

其余代码在语法上是正确的,但必须重写,因为在编写正则表达式以处理整个代码时,它会对输入中的单词进行迭代。对于该正则表达式,处理过程需要依次获取每个参数,或者命令行需要组合为字符串。

有关该问题代码的更多评论将在下面进一步发布。

让我提供一种不同的方法

use warnings;
use strict;
use feature 'say';

use Scalar::Util qw(looks_like_number);

my ($n1, $op, $n2) = @ARGV;

my $re_oper = qr{^(?:\+|-|\*|/)$};  #/

usage() if @ARGV != 3
    or not looks_like_number($n1) or not looks_like_number($n2)
    or $op !~ $re_oper;

my %calculate = ( 
    '+' => sub { return $_[0] + $_[1] },
    '-' => sub { return $_[0] - $_[1] },
    '*' => sub { return $_[0] * $_[1] },
    '/' => sub {
        die "Can't divide by zero" if $_[1] == 0;
        return $_[0] / $_[1] 
    },
);

say $calculate{$op}->($n1, $n2);

sub usage { 
    say STDERR 
        "Usage: $0 number operator number\n",
        "The \"operator\" is one of +,-,\\*,/\n",
        "Note that multiplication (*) must be escaped at command line";
    exit;
}
Run Code Online (Sandbox Code Playgroud)

这些参数被复制并检查使用错误looks_like_number标量::的Util一个正则表达式,和。我使用qr运算符准备了正则表达式模式,以便分别指定它,这使测试,维护和扩展变得更加容易。

然后,我们定义一个带有匿名子例程作为值的哈希(它们是代码引用,请参阅在perlref中创建引用)中的4项),通常称为调度表。因此,不需要级联序列:对于给定的键(运算符),将取消引用相应的值,并且该子例程将运行。 if-elsif

为了简洁起见,在一个语句中检查了参数,但最好一一完成,以便能够向用户报告所产生的确切错误(并引用引起错误的输入)。


发布代码的更多细节

  • 操作员捕获的图案需要在使用测试字符串比较,有什么用做eq运营商,而不是用==在perlop中看到这些。所以,if ($2 eq '+')

  • regex匹配默认情况下是针对$_变量进行的,该变量是Perl中许多事物的默认值。因此,有没有必要写if ($_ =~ /.../),但只if (/.../)。要清楚了解(隐式)使用的理由,这一点要清楚得多。$_

  • 正则表达式模式\d匹配所有数字,包括Unicode。更好地使用[0-9]

  • 该模式\s允许一个 “空白”字符。如果您将命令行参数组合为一个字符串$input = join ' ', @ARGV;(以便在其上使用正则表达式),那很好。但是,使用允许更多的空间仍然更安全\s+

  • .在正则表达式匹配任意一个字符。这限制了脚本将来可能的扩展,例如提高功率**(等)。考虑允许数字之间的任何字符,这是可行的,因为它们在命令行上由空格分隔。

  • 用于数字的模式仅与正整数匹配(同时如果第一个数字为负号,则偶然允许其为负数)。上面的代码通过使用来解决这一问题looks_like_number,它利用了Perl可以做的所有事情来识别数字。


问题的原始版本具有while (<>)循环功能,答案始于此

“钻石”运算符(<>从提交的命令行上文件中读取

输入来自<>标准输入,或来自命令行上列出的每个文件。

因此您的输入将5 + 2被视为文件名5+2,并且此类文件不存在。

该运算符的复杂性更高,您可以在文档中阅读该运算符。

可以在@ARGV中访问命令行参数,因此最简单的解决方案是将替换为while (<>)foreach (@ARGV)然后逐项处理术语。