Re: '+' to +
by ambrus (Abbot) on May 29, 2005 at 13:01 UTC
|
#!perl
use warnings;
use strict;
my %arith;
$arith{"+"} = sub { $_[0] + $_[1] };
$arith{"-"} = sub { $_[0] - $_[1] };
$arith{"*"} = sub { $_[0] * $_[1] };
$arith{"/"} = sub { $_[0] / $_[1] };
my $Problem = "6 * 8";
$Problem =~ /^(\d*)\s(.)\s(\d*)$/;
my $Number1 = $1;
my $Number2 = $3;
my $Operator = $2;
my $Op = $arith{$2} or die qq[wrong operator: "$Operator"];
my $Answer;
$Answer = &$Op($Number1, $Number2);
print $Answer, "\n";
__END__
And take care with division by zero errors. | [reply] [d/l] |
|
use warnings;
use strict;
my %arith;
$arith{"+"} = sub { $_[0] + $_[1] };
$arith{"-"} = sub { $_[0] - $_[1] };
$arith{"*"} = sub { $_[0] * $_[1] };
$arith{"/"} = sub { $_[0] / $_[1] };
my $problem = "6 + 1";
my $answer;
if (
$problem =~ /^(\d+)\s*(\S)\s*(\d+)$/
and exists $arith{$2}
) {
eval {
$answer = $arith{$2}->( $1, $3 );
}
} else {
die "Oops!\nOperation syntax not understood.\n";
}
die "Woops!\n$@" if $@;
print "$answer\n";
- Modified regexp to require two operands and to make whitespace optional.
- Added basic validity/syntax checking to the results of the pattern match.
- Trapped (and dealt with) possible errors resulting from mathematical operation using the block version of eval. Note, this is the safe version, we're not evaluating quoted text, we're evaluating known code to trap potential errors resulting from mathematical operation errors such as divide by zero.
- Added more robust checking for the existance of a particular mathematical operator in the hashtable.
- Switched to $funcref->(params) notation instead of &$funcref(params).
It's a long way from being a full fledged algeraic expression parser, but it is a little more robust.
| [reply] [d/l] |
|
my %arith = map {
$_ => eval sprintf 'sub { $_[0] %s $_[1] }', $_
} qw[ + - * / ];
ihb
See perltoc if you don't know which perldoc to read!
| [reply] [d/l] |
|
| [reply] |
Re: '+' to +
by eyepopslikeamosquito (Archbishop) on May 29, 2005 at 13:35 UTC
|
| [reply] |
|
| [reply] |
Re: '+' to +
by monarch (Priest) on May 29, 2005 at 12:16 UTC
|
I'm sure there are better ways than the one I'm about to suggest.
Firstly you've used a regexp to clean up the input, very good, you're protecting yourself against potentially nasty input from the source.. because real-world programmers know real-world people try and stick anything into our machines!
Perhaps you could make the regexp ensure the operator is only one of the four specified operations and then performing an eval on the reconstructed operation?
e.g.
$Problem =~ /(\d+) # one or more digits
\s* # zero or more whitespace
([-+\/*]) # operator (1 char only)
\s* # zero or more whitespace
(\d+)/x; # one or more digits
my $Number1 = $1;
my $Number2 = $3;
my $Operator = $2;
my $Answer;
my $Code = "$1 $2 $3"; # we're confident this is safe
$Answer = eval "$Code";
Of course if statements should be littered about to ensure the regexp actually matches etc.
Update: as wfsp pointed out below I have now escaped the forward slash in the regexp because the forward slash is the regexp delimiter. | [reply] [d/l] [select] |
|
if ( my ($Code) = $Problem =~ /^\s*(\d+\s*[-+/*]\s*\d+)\s*=\s*$/ ) {
if ( defined ( my $Answer = eval $Code ) ) {
# do stuff with $Answer
} else {
# do stuff with $@
}
} else {
# Complain about invalid format
}
I'd even tend to be a bit more liberal with my laundering to let though any simple arithmetic /^([-+/*\s\d().]+)=\s*$/. | [reply] [d/l] [select] |
|
I should explain that this snippet is from a little client/server math game I'm writing for my kids to practice their math skills. Therefore I know the format of $Problem precisely because I generated it in the game server. That's why I didn't bother with a lot of validation.
What I am looking for is a cool way to convert the string '+' to the operator +.
| [reply] |
Re: '+' to +
by wfsp (Abbot) on May 29, 2005 at 12:51 UTC
|
I believe checking a regex for failure can prevent many headaches later.
#!/usr/bin/perl
use strict;
use warnings;
my $problem = '1 / 2 = ';
my ($number1, $number2, $operator);
if ($problem =~ /(\d+)\s*([-+*\/])\s*(\d+)/){
($number1, $number2, $operator) = ($1, $2, $3);
print "$number1 $number2 $operator\n";
}
else{
print "regex failed\n";
}
I also agree with monarch that as you know what the operator can be you can use a character class.
FWIW. I always have to check the docs to check which characters need escaping. They say that:
The special characters for a character class are -]\^$ and are matched using an escape...
Now I'm sure that's a backslash in there. But I couldn't get the regex above to work without escaping the forward slash.
Anyone see what I'm missing here?
Update:
It's because I used the forward slash as the quote for the regex!
Many thanks to the monks in the CB.
| [reply] [d/l] |
Re: '+' to +
by polettix (Vicar) on May 29, 2005 at 23:06 UTC
|
Maybe it's a little overkill, but if you plan making your grammar a bit more - er - complicated you can take a look to Parse::RecDescent and satellite stuff. In particular, a problem very similar to yours is dealt with in this tutorial at perl.com.
Flavio (perl -e 'print(scalar(reverse("\nti.xittelop\@oivalf")))')
Don't fool yourself.
| [reply] |
Re: '+' to +
by sh1tn (Priest) on May 29, 2005 at 17:03 UTC
|
my $problem = qr{
(\d+
\s+
[\+|\-|\*|\/]
\s+
\d+)
}x;
my $evaluation = '10 / 2';
{
local $_ = $evaluation;
s/$problem/$1/ee;
print
}
STDOUT: 5
| [reply] [d/l] |
Re: '+' to +
by ambrus (Abbot) on May 30, 2005 at 08:24 UTC
|
This one involves a bit of magic, but it does not use eval,
nor does it explicitly list the four operators.
use Math::BigFloat; $big = Math::BigFloat->bzero;
$Problem = "12 - 5";
$Problem =~ /^(\d+)\s(\S)\s(\d+)$/; ($Number1, $Operator, $Number2) =
+($1, $2, $3);
$Answer = ($big + $Number1)->${\("(" . $Operator)}($Number2);
print $Answer, "\n";
| [reply] [d/l] |
|
use Math::Complex; $big = 0*i;
$Problem = "6 * 8";
$Problem =~ /^(\d+)\s(\S)\s(\d+)$/; ($Number1, $Operator, $Number2) =
+($1, $2, $3);
$Answer = ($big + $Number1)->${\("(" . $Operator)}($Number2);
print $Answer, "\n";
| [reply] [d/l] |
Re: '+' to +
by NateTut (Deacon) on May 31, 2005 at 15:17 UTC
|
Thank you all! I knew that there would be at least one TMTOWTDI.
I need to learn more about eval, I've used it primarly to trap errors.
I must admit I'm still trying to grok ambrus's solution... | [reply] |