我需要在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。任何人都可以帮我确定我做错的地方吗?
答案 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可以识别数字的所有内容。