RPN Take 4

amahabal on 2009-03-03T18:52:07

fREW++ and pmichaud++ have, between them, three versions of the RPN calculator. Here is a fourth, clener in some ways.

You can use it as follows:

>  ./perl6 rpn.pl '4 5 9 sqrt + *'
32
> ./perl6 rpn.pl '4 5 9 sqrt rotate3 + *'
35


And here is the code:

my %op_dispatch_table = {
        '+'    => { $^a + $^b },
        '-'    => { $^a - $^b },
        '*'    => { $^a * $^b },
        '/'    => { $^a / $^b },
        'sqrt' => { $^a.sqrt },
        'say'  => { $^a.say; $^a},
        'rotate3' => { ($^b, $^c, $^a) },
    };
  
sub evaluate (%odt, $expr) {
  my @stack;
  my @tokens = $expr.split(/\s+/);
  for @tokens {
      when /^\d+$/     { @stack.push($_); }
      when ?%odt{$_} { my $f = %odt{$_};
                       my @top_terms = gather {
                         for 1..$f.arity { take @stack.pop}
                       };
                       @top_terms = reverse(@top_terms);
                       my @to_push = $f.(|@top_terms);
                       @stack.push(@to_push);
                     }
      default        { die "Unrecognized token '$_'; aborting"; }
   }
   @stack.pop;
}

say "Result: { evaluate(%op_dispatch_table, @*ARGS[0]) }";


I'd like to do even better: when an operator is unrecognized (say "double"), but there is a subroutine of that name in scope we should use that. I will try that next.


nice and clear

mpeters on 2009-03-03T20:54:03

I have to say that I've seen the other examples of this and I think this is definitely the cleanest and easiest to understand. Using $^a, $^b and $^c with the arity checks definitely beats all those pops, shifts, etc.

but just one thing more

dpuu on 2009-03-03T23:07:29

I agree, though I think it might need a special-case for zero-arity so that could could write an arbitrary reduction:

    'sum' => { @stack = [+] @stack }, ...

hmm, can that be written as assignment-operator?

    'sum' => { @stack [+]= () },

Re:but just one thing more

amahabal on 2009-03-04T04:12:48

I was hoping to use zero arity like so:

'pi' => { 3.1415926 }

Perhaps

'sum' => { @stack = [+]@stack; } but WholeStack,

or something along those lines would work.