Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

pmichaud (6013)

pmichaud
  (email not shown publicly)
http://www.pmichaud.com/

Patrick Michaud is the pumpking for the Rakudo Perl 6 compiler. He holds a Ph.D. in Computer Science and was formerly a Professor of Computer Science at Texas A&M University-Corpus Christi. He is currently a software developer and consultant focused on open source development and applications, including Perl, PmWiki, and Linux.

Journal of pmichaud (6013)

Monday March 02, 2009
02:41 PM

a(nother) Reverse Polish Notation Calculator

[ #38580 ]

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 = ...' parts of subtraction and division. Maybe I'll implement meta-R now... :-)

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]) }";

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
 Full
 Abbreviated
 Hidden
More | Login | Reply
Loading... please wait.
  • That's much more compact! I'll try to absorb these changes so that next time I make a post about perl6 it will be closer to The Essence of Perl 6. :-)
    --
    --fREW
    http://blog.afoolishmanifesto.com
  • I don't know how to do this, but I am sure Patrick or Jonathan can do this quickly... In the code above, there is still repetition: all entries in %op_dispatch_table are nearly identical, differing only depending on the arity. We could replace that with something generic which is passed a sub, it pops as many elements as the arity of the passed sub and pushes back the value of applying the sub to the reverse of this list. I was unable to do this myself because I could not get from the name of the sub to t
    • 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

      • I have one version here: http://use.perl.org/~amahabal/journal/38583 [perl.org] 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.