Taking strnig as input for calculator program in Perl

大憨熊 提交于 2021-01-28 10:20:52

问题


I am new to Perl and I'm trying to create a simple calculator program, but the rules are different from normal maths. All operations have the same power and the math problem must be solved from left to right. Here is an example:

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

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

So in the first case the user needs to enter one string: 123 - 10 + 4 * 10. How do i approach this task? I'm sorry if it's too much of a general question, but i'm not sure how to even begin. Do i need a counter? Like - every second character of the string is an operator, while the two on the sides are digits.


回答1:


I'm afraid I'm lazy so I'll parse with a regex and process as I parse.

#!/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;

Note the use of (?{...}) 1




回答2:


To be clear, you are not looking for a regular calculator. You are looking for a calculator that bends the rules of math.

What you want is to extract the operands and operators, then handle them 3 at the time, with the first one being the rolling "sum", the second an operator and the third an operand.

A simple way to handle it is to just eval the strings. But since eval is a dangerous operation, we need to de-taint the input. We do this with a regex match: /\d+|[+\-*\/]+/g. This matches either 1 or more + digits \d or |, 1 or more + of either +-*/. And we do this match as many times as we can /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")
}

You may wish to add some input checking, to count the args and make sure they are the correct number.

A more sensible solution is to use a calling table, using the operator as the key from a hash of subs designed to handle each math operation:

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

As long as the middle argument is an operator, this will perform the required operation without the need for eval. This will also allow you to add other math operations that you might want for the future.




回答3:


Just to read raw input from the user you would simply read the STDIN file handle.

$input = <STDIN>;

This will give you a string, say "123 + 234 - 345" which will have a end of line marker. You can remove this safely with the chomp command.

After that you will want to parse your string to get your appropriate variables. You can brute force this with a stream scanner that looks at each character as you read it and processes it accordingly. For example:

@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
  }...
}

Another approach would be to split it into words so you can just deal with whole terms.

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

Instead of a stream of characters, as you process the array values will be 123, +, 234, -, and 345...

Hope this points you in the right direction...



来源:https://stackoverflow.com/questions/64833828/taking-strnig-as-input-for-calculator-program-in-perl

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!