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]) }";
The result:
$
./perl6 calc.pl '5 6 +'
Result: 11
The major changes I made to fREW's code:
1. Convert the explicit subs into simple closures, assuming that the stack is the implicit topic.
2. Use 'when' statements instead of if/elsif/else in the body of the 'evaluate' sub.
And yes, as frew points out -- when we get the 'R' metaoperator in place we can get rid of the 'my $s =
Thanks fREW for the very interesting example!
Pm
Update: I went ahead and implemented the R metaoperator. For those who aren't familiar with meta-R, it reverses the order of the operands to an operator. So, (5 R- 4) is the same as (4 - 5), and (2 R/ 10) is the same as (10 / 2). With this change, the RPN calculator becomes:
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]) }";
Wow! (Score:1)
--fREW
http://blog.afoolishmanifesto.com
Making it even more compact... (Score:1)
Re: (Score:2)
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):
I can also imagine functions that rotate or swap the top N operands, or the like. Trying to wedge those into
Re: (Score:1)