In a Reverse Polish Notation Calculator, fREW Schmidt takes the RPN calculator example from Higher Order Perl and converts it over to Perl 6.
Very cool.
But as usual, I visually look at the Perl 6 version compared to the Perl 5 version and think "Can't we do better?" In this case, we can. Here's my version of the same code:
my %op_dispatch_table = { '+' => { .push(.pop + .pop) }, '-' => { my $s = .pop; .push(.pop - $s) }, '*' => { .push(.pop * .pop) }, '/' => { my $s = .pop; .push(.pop / $s) }, 'sqrt' => { .push(.pop.sqrt) }, };
sub evaluate (%odt, $expr) { my @stack; my @tokens = $expr.split(/\s+/); for @tokens { when /\d+/ { @stack.push($_); } when ?%odt{$_} { %odt{$_}(@stack); } default { die "Unrecognized token '$_'; aborting"; } } @stack.pop; }
say "Result: { evaluate(%op_dispatch_table, @*ARGS[0]) }";
$ ./perl6 calc.pl '5 6 +' Result: 11
my %op_dispatch_table = { '+' => { .push(.pop + .pop) }, '-' => { .push(.pop R- .pop) }, '*' => { .push(.pop * .pop) }, '/' => { .push(.pop R/ .pop) }, 'sqrt' => { .push(.pop.sqrt) }, };
sub evaluate (%odt, $expr) { my @stack; my @tokens = $expr.split(/\s+/); for @tokens { when /\d+/ { @stack.push($_); } when ?%odt{$_} { %odt{$_}(@stack); } default { die "Unrecognized token '$_'; aborting"; } } @stack.pop; }
say "Result: { evaluate(%op_dispatch_table, @*ARGS[0]) }";
Re:Making it even more compact...
pmichaud on 2009-03-03T15:09:21
Yes, it could potentially be made slightly shorter... but I like the straightforward clarity of the above. An advantage of the above approach is that it would work for almost any stack pattern -- including things that don't fit the ".push( fn(.pop) )" model. For example, to add a 'say' operator that displays the top value of the stack (without removing it):
'say' => {
.[*-1].say } I can also imagine functions that rotate or swap the top N operands, or the like. Trying to wedge those into a standard
.push/.pop sequence seems a little less straightforward than what we have above. I'd still be interested in seeing the solution you have in mind, even if Rakudo doesn't compile/run it yet. In fact, we'd be a bit more likely to make it do that if we had an example to work to.
:-) Thanks!
Pm
Re:Making it even more compact...
amahabal on 2009-03-03T18:58:57
I have one version here:
http://use.perl.org/user/amahabal/journal/38583
Thanks to your suggestion, I added 'say' and 'rotate3'. I will try my original idea next, for which I need to go from a name (such as 'double') to a callable object (&double). Not sure yet what I'd do with multis and such, but I will see how far I get.