问题
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