aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordrbaggy <js5@sanger.ac.uk>2021-12-14 21:11:13 +0000
committerdrbaggy <js5@sanger.ac.uk>2021-12-14 21:11:13 +0000
commitc12a27d57313357ebab130cfcff7ae9cf1c058d3 (patch)
tree1a8d4eca7cd1d72dae4cf61369cbf8c8279f36d8
parent6a1bfd09b0b9869dfeba6f1a9d183d3c884c984d (diff)
downloadperlweeklychallenge-club-c12a27d57313357ebab130cfcff7ae9cf1c058d3.tar.gz
perlweeklychallenge-club-c12a27d57313357ebab130cfcff7ae9cf1c058d3.tar.bz2
perlweeklychallenge-club-c12a27d57313357ebab130cfcff7ae9cf1c058d3.zip
add rpn tokenizer
-rw-r--r--challenge-143/james-smith/perl/ch-1.pl59
1 files changed, 57 insertions, 2 deletions
diff --git a/challenge-143/james-smith/perl/ch-1.pl b/challenge-143/james-smith/perl/ch-1.pl
index a4abfdeaab..2b0c32a29a 100644
--- a/challenge-143/james-smith/perl/ch-1.pl
+++ b/challenge-143/james-smith/perl/ch-1.pl
@@ -8,6 +8,22 @@ use Test::More;
use Benchmark qw(cmpthese timethis);
use Data::Dumper qw(Dumper);
+my(@s,@o);
+
+## List of operators - Entries in each array are:
+## * precedence of operator
+## * function to be run when finding operator in infix stream
+## * function to be run when finding operator in rpn stream
+
+my %f;
+%f = (
+ '('=>[0,sub{push@s,'(' }, ],
+ ')'=>[0,sub{push@o,$_ while($_=pop@s)ne'('}, ],
+ '*'=>[2,sub{push@o,pop@s while@s&&$f{$s[-1]}[0]>1;push@s,'*'},sub{$s[-2]*=pop@s}],
+ '+'=>[1,sub{push@o,pop@s while@s&&$f{$s[-1]}[0]; push@s,'+'},sub{$s[-2]+=pop@s}],
+ '-'=>[1,sub{push@o,pop@s while@s&&$f{$s[-1]}[0]; push@s,'-'},sub{$s[-2]-=pop@s}],
+);
+
my @TESTS = (
[ '10 + 20 - 5', 25 ],
[ '(10 + 20 - 5) * 2', 50 ],
@@ -17,10 +33,14 @@ my @TESTS = (
[ '(10 + 10) * (10 - 10) * (10 + 10) * (20 - (((20))))', 0 ],
);
-is( evaluate($_->[0]), $_->[1] ) foreach @TESTS;
-is( eval( $_->[0]), $_->[1] ) foreach @TESTS;
+is( evaluate( $_->[0]), $_->[1] ) foreach @TESTS;
+is( evaluate_via_rpn($_->[0]), $_->[1] ) foreach @TESTS;
+is( evaluate_rpn_hsh($_->[0]), $_->[1] ) foreach @TESTS;
+is( eval( $_->[0]), $_->[1] ) foreach @TESTS;
cmpthese( 20000, {
'evaluate' => sub { evaluate($_->[0]) for @TESTS },
+ 'eval_rpn' => sub { evaluate_via_rpn($_->[0]) for @TESTS },
+ 'eval_hsh' => sub { evaluate_rpn_hsh($_->[0]) for @TESTS },
'eval' => sub { eval( $_->[0]) for @TESTS },
});
done_testing();
@@ -33,3 +53,38 @@ sub evaluate {
return $str;
}
+sub evaluate_via_rpn {
+ @s=();@o=();
+
+ for ( $_[0] =~ m{(-?\d+|[-+*()])}g ) {
+ if( m{\d} ) {
+ push @o, $_;
+ } elsif( $_ eq '(' ) {
+ push @s, $_;
+ } elsif( $_ eq ')' ) {
+ push @o, $_ while ($_ = pop @s) ne '(';
+ } else {
+ push @o, pop @s while @s && $f{$s[-1]}[0] && $f{$_}[0]<=$f{$s[-1]}[0];
+ push @s, $_;
+ }
+ }
+ push @o, reverse @s;
+
+ @s = ();
+ ## Now evaluate the RPN tree...
+ $_ eq '+' ? $s[-2] += pop @s
+ : $_ eq '-' ? $s[-2] -= pop @s
+ : $_ eq '*' ? $s[-2] *= pop @s
+ : (push @s, $_) for @o;
+ $s[0];
+}
+
+## Code from hell... First for loop processes the infix into rpn
+## Second for loop processes the rpn stream
+sub evaluate_rpn_hsh {
+ @o=(); @s=();
+ ($f{$_}) ? (&{$f{$_}[1]}) : (push@o,$_) for $_[0] =~ m{(-?\d+|[-+*()])}g;
+ ($f{$_}) ? (&{$f{$_}[2]}) : (push@s,$_) for @o, reverse splice @s,0;
+ $s[0];
+}
+