使用 perl 创建一个简单的计算器 [英] using perl to create a simple calculator

查看:29
本文介绍了使用 perl 创建一个简单的计算器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要在 Perl 中设计一个名为 ex1.pl 的简单计算器,其中只有 +-*/ 函数.以下是我所拥有的.

while (@ARGV>0) {如果 ($_=~m/(\d+)\s(.)\s(\d+)/) {如果 ($2 == "+") {打印 "$1 + $3\n";}elsif ($2 == "-") {打印 "$1 - $3\n";}elsif ($2 == "*") {打印 "$1 * $3\n";}elsif ($2 == "/") {如果($3 == 0){print "$1 不能被 0\n 整除";}别的 {打印 "$1/$3\n";}}别的 {打印操作员未识别\n";}}别的 {打印语法错误\n";}}

例如,如果我输入./ex1.pl 5 + 2,它不断报告错误

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

直到我按下 ctrl + c.谁能帮我找出我做错了什么??

解决方案

循环 while(@ARGV>0) 没有分配给 $_正则表达式匹配完成,所以没有匹配,代码转到 else.此外,@ARGV 永远不会被清空,因此代码处于无限循环中.

其余代码在语法上是正确的,但必须重写,因为它在编写正则表达式以处理整个输入时迭代输入中的单词.要么处理需要依次接受每个参数,要么命令行需要组装成一个字符串,用于该正则表达式.

对问题中的代码的进一步评论在下面进一步发布.

让我提供一个不同的方法

使用警告;使用严格;使用功能说";使用 Scalar::Util qw(looks_like_number);我的 ($n1, $op, $n2) = @ARGV;我的 $re_oper = qr{^(?:\+|-|\*|/)$};#/用法() 如果@ARGV != 3或不look_like_number($n1) 或不looks_like_number($n2)或 $op !~ $re_oper;我的 %calculate = ('+' =>sub { 返回 $_[0] + $_[1] },'-' =>sub { return $_[0] - $_[1] },'*' =>sub { return $_[0] * $_[1] },'/' =>子{如果 $_[1] == 0,死不能被零除";返回 $_[0]/$_[1]},);说 $calculate{$op}->($n1, $n2);子用法{说 STDERR"用法:$0 number operator number\n","\"operator\" 是 +,-,\\*,/\n" 之一,"请注意乘法 (*) 必须在命令行中转义";出口;}

使用来自 Scalar::Util 和一个正则表达式.我使用 qr 运算符准备正则表达式模式 以便单独指定,使测试、维护和扩展更容易.

然后我们用匿名子例程作为值定义一个散列(它们是代码引用,见在 perlref 中进行引用),通常称为 调度表.因此不需要级联 if-elsif 系列:对于给定的键(运算符),相应的值被取消引用,并且该子例程运行.

为简洁起见,在一个语句中检查参数,但最好逐个进行检查,以便能够向用户报告发生的确切错误(以及导致错误的引用输入).

<小时>

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

  • 捕获的操作符模式需要使用string比较来测试,用eq操作符做了什么,而不是==.在 perlop 中查看这些in perlop.所以if ($2 eq '+') etc

  • 默认情况下,正则表达式匹配是针对 $_ 变量,Perl 中很多东西的默认值.所以不需要写if ($_ =~/.../) 而是只写if (/.../).阅读证明(隐式)使用 $_

  • 的理由要清楚得多
  • 正则表达式模式 \d 匹配各种数字,包括 Unicode.更好地使用 [0-9]

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

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

  • 用于数字的模式只匹配正整数(如果前面有减号,则意外地允许第一个为负).上面的代码通过使用 looks_like_number 来解决这个问题,它利用 Perl 所能做的所有事情来识别数字.

<小时>

问题的原始版本while (<>) 用于循环,答案以此开头:

菱形"运算符(<>) 从命令行提交的文件中读取

<块引用>

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

所以您的输入 5 + 2 被视为文件名 5+2,等等文件不存在.

这个运算符比较复杂,可以在文档中阅读.

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

I need to design a simple calculator in Perl called ex1.pl with only +, -, *, and / functions. Below is what I have.

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";
    }   
}

For example, if I type ./ex1.pl 5 + 2, It keeps reporting errors that

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

until I press ctrl + c. Can anyone help me identify where I did wrong??

解决方案

The loop while(@ARGV>0) doesn't assign to $_ on which the regex match is done, so nothing is matched and the code goes to else. Also, @ARGV is never emptied so the code is in the infinite loop.

The rest of the code is syntactically correct but has to be rewritten, since it iterates over words in input while the regex is written to process the whole of it. Either the processing needs to take each argument in turn, or the command line need be assembled into a string, for that regex.

Further comments on the code in the question are posted further below.

Let me offer a different approach

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;
}

The arguments are copied and checked for errors using looks_like_number from Scalar::Util, and a regex. I prepare the regex pattern using the qr operator so that it is specified separately, what makes testing, maintenance, and expansion easier.

Then we define a hash with anonymous subroutines as values (they are code references, see item 4. in Making references in perlref), often called a dispatch table. Thus there is no need for a cascading if-elsif series: For a given key (the operator) the corresponding value is dereferenced and that subroutine runs.

The arguments are checked in one statement for brevity, but this is better done one by one so to be able to report to the user the exact error made (and quote input that caused it).


A few more specifics on the posted code

  • Captured pattern for the operator need be tested using string comparison, what is done with eq operator, not with ==. See these in perlop. So if ($2 eq '+') etc

  • A regex match is by default done against the $_ variable, the default for many things in Perl. So there is no need to write if ($_ =~ /.../) but just if (/.../). This is far clearer to read what justifies the (implicit) use of $_

  • Regex pattern \d matches all kinds of digits, Unicode included. Better use [0-9]

  • The pattern \s allows for one "whitespace" character. If you combine the command line arguments into a string by $input = join ' ', @ARGV; (so to use regex on it) then that is fine. But it is still safer to allow for more spaces with \s+.

  • The . in regex matches any one character. This limits possible future expansion of the script, for example to raise to power ** (etc). Consider allowing any characters between numbers, what is feasible as they are delimited on the command line by space.

  • The pattern used for numbers matches only positive integers (while accidentally allowing the first one to be negative if a minus precedes it). The code above takes care of this by using looks_like_number, which utilizes all that Perl can do to recognize a number.


The question's original version had while (<>) for the loop, and the answer started with this:

The "diamond" operator (<>) reads from files submitted on the command line

Input from <> comes either from standard input, or from each file listed on the command line.

so your input 5 + 2 is taken to be filenames 5 and + and 2, and such files don't exist.

There is more complexity to this operator, about which one can read in the documentation.

The command line arguments can be accessed in @ARGV, so the simplest solution is to replace the while (<>) with foreach (@ARGV) and then process term by term.

这篇关于使用 perl 创建一个简单的计算器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆