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

时间:2018-05-30 06:18:12

标签: perl unix

我需要在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";
    }   
}

例如,如果我输入./ex1.pl 5 + 2,它会一直报告错误

Use of uninitialized value in pattern match (m//) at line 4

直到我按ctrl + c。任何人都可以帮我确定我做错的地方吗?

1 个答案:

答案 0 :(得分:4)

注意此答案的开头介绍了自此编辑的问题的原始版本,该版本的循环次数为while (<>)。无论发生什么变化,其余的答案都适用。

"diamond" operator (<>)从命令行上提交的文件中读取

  

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

因此您的输入5 + 2被视为文件名5+以及2,并且这些文件不存在。

此运算符的复杂性更高,关于哪个可以在文档中阅读。

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

问题的修改使循环进入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;
}

使用Scalar::Util中的looks_like_number和正则表达式复制参数并检查错误。我使用qr operator准备正则表达式模式,以便单独指定,使维护和扩展更容易。

然后我们使用匿名子例程(代码引用,参见Making references in perlref中的项 4。)定义哈希值作为值,通常称为dispatch table。因此,不需要级联if-elsif系列:对于给定的键(操作符),相应的值被解除引用并且该子例程运行。

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

有关已发布代码的更多细节

  • 需要使用 string 比较来测试运算符的捕获模式,使用eq运算符进行测试,而不是使用==。请参阅这些in perlop。 所以if ($2 eq '+')

  • 默认情况下,对$_ variable执行正则表达式匹配,这是Perl中许多内容的默认值。因此,无需编写if ($_ =~ /.../),只需编写if (/.../)。阅读$_

  • (隐含)使用的理由更为清楚
  • 正则表达式模式\d捕获各种数字,包括Unicode。更好地使用[0-9]

  • 模式\s允许一个&#34;空白&#34;字符。如果将命令行参数组合成$input = join ' ', @ARGV;的字符串(以便在其上使用正则表达式),那就没问题了。但是使用\s+允许更多空格仍然更安全。

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

  • 用于数字的模式只匹配正整数(如果在它之前有一个减号,则意外地允许第一个为负数)。上面的代码通过使用looks_like_number来解决这个问题,{{1}}利用Perl可以识别数字的所有内容。

相关问题