我需要设计一个简单的计算器在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。谁能帮助我确定我做错了什么地方?
循环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)然后逐项处理术语。