【问题标题】:Taking strnig as input for calculator program in Perl在 Perl 中将字符串作为计算器程序的输入
【发布时间】:2021-02-26 05:22:11
【问题描述】:

我是 Perl 新手,我正在尝试创建一个简单的计算器程序,但规则与普通数学不同。所有运算都具有相同的功率,数学问题必须从左到右解决。 这是一个例子:

123 - 10 + 4 * 10 = ((123 - 10) + 4) * 10 = 1170

8 * 7 / 3 + 2 = ((8 * 7) / 3) + 2 = 20.666

所以在第一种情况下,用户需要输入一个字符串:123 - 10 + 4 * 10。 我如何处理这项任务? 如果这是一个笼统的问题,我很抱歉,但我不知道如何开始。我需要柜台吗? Like - 字符串的第二个字符是一个运算符,而两边的两个是数字。

【问题讨论】:

    标签: string perl input calculator


    【解决方案1】:

    恐怕我很懒,所以我会用正则表达式解析并在解析时进行处理。

    #!/usr/bin/env perl
    
    #use Data::Dumper;
    use Params::Validate (':all');
    use 5.01800;
    use warnings;
    
    my $string=q{123 - 10 + 4 * 10};
    
    my $result;
        sub fee {
            my ($a)=validate_pos(@_,{ type=>SCALAR });
            #warn Data::Dumper->Dump([\$a],[qw(*a)]),' ';
            $result=$a;
            };
        sub fi {
            my ($op,$b)=validate_pos(@_,{ type=>SCALAR},{ type=>SCALAR });
            #warn Data::Dumper->Dump([\$op,\$b],[qw(*op *b)]),' ';
            $result = $op eq '+' ? $result+$b :
                      $op eq '-' ? $result-$b :
                      $op eq '*' ? $result*$b :
                      $op eq '/' ? $result/$b :
                      undef;
            #warn Data::Dumper->Dump([\$result],[qw(*result)]),' ';
            };
    
    $string=~ m{^(\d+)(?{ fee($1) })(?:(?: *([-+/*]) *)(\d+)(?{ fi($2,$3) }))*$};
    say $result;
    

    注意使用 (?{...}) 1

    【讨论】:

      【解决方案2】:

      需要明确的是,您不是在寻找常规计算器。您正在寻找一个打破数学规则的计算器。

      您想要的是提取操作数和运算符,然后同时处理它们 3 个,第一个是滚动“和”,第二个是运算符,第三个是操作数。

      处理它的简单方法是只使用eval 字符串。但是由于eval 是一个危险的操作,我们需要对输入进行去污点。我们通过正则表达式匹配来做到这一点:/\d+|[+\-*\/]+/g。这匹配 1 个或多个 + 数字 \d|,1 个或多个 ++-*/。我们会尽可能多地进行此匹配/g

      use strict;
      use warnings;
      use feature 'say';
      
      while (<>) {                                     # while we get input
          my ($main, @ops) = /\d+|[+\-*\/]+/g;         # extract the ops
          while (@ops) {                               # while the list is not empty
              $main = calc($main, splice @ops, 0, 2);  # take 2 items off the list and process
          }
          say $main;                                   # print result
      }
      
      sub calc {
          eval "@_";      # simply eval a string of 3 ops, e.g. eval("1 + 2")
      }
      

      您可能希望添加一些输入检查,以计算 args 并确保它们是正确的数字。

      一个更明智的解决方案是使用调用表,使用运算符作为键,从设计用于处理每个数学运算的子哈希中:

      sub calc {
          my %proc = (
              "+" => sub { $_[0] + $_[1] },
              "-" => sub { $_[0] - $_[1] },
              "/" => sub { $_[0] / $_[1] },
              "*" => sub { $_[0] * $_[1] }
          );
          return $proc{$_[1]}($_[0], $_[2]);
      }
      

      只要中间参数是一个运算符,这将执行所需的操作,而不需要eval。这也将允许您添加将来可能需要的其他数学运算。

      【讨论】:

      • 这太好了,非常感谢您的帮助。我现在要寻找一种方法来扭转它——让它从右到左,作为一个子任务。例如 - 123 - 10 + 4 * 10 = 123 - ( 10 + ( 4 * 10 ) ) = 73。
      • 不客气。如果这解决了您的问题,请务必点击接受按钮。
      • 反向实际上并不是一个挑战,只需对来自字符串的参数使用reverse 函数...my ($main, @ops) = reverse / ... /g
      【解决方案3】:

      只需读取用户的原始输入,您只需读取 STDIN 文件句柄即可。

      $input = <STDIN>;
      

      这会给你一个字符串,比如“123 + 234 - 345”,它会有一个行尾标记。您可以使用 chomp 命令安全地删除它。

      之后,您将需要解析字符串以获取适当的变量。您可以使用流扫描器强制执行此操作,该扫描器在您阅读每个字符时查看它并相应地处理它。例如:

      @input = split //, $input;
      for $ch (@input) {
        if ($ch > 0 and $ch <= 9) {
          $tVal = ($tVal * 10) + $ch;
        } elsif ($ch eq " ") {
          $newVal = $oldVal
        } elsif ($ch eq "+") {
          # Do addition stuff
        }...
      }
      

      另一种方法是将其拆分为单词,这样您就可以处理整个术语。

      @input = split /\s+/, $input;
      

      在处理时,数组值将是 123、+、234、- 和 345,而不是字符流...

      希望这会为您指明正确的方向...

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-16
        • 2021-09-04
        • 2012-11-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多