Skip to content

Instantly share code, notes, and snippets.

@practicingruby
Created July 12, 2012 15:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save practicingruby/3098842 to your computer and use it in GitHub Desktop.
Save practicingruby/3098842 to your computer and use it in GitHub Desktop.
Code Golf RPN Calculator!
p((_=[gets.split,[],"+-**/"])[0].map{|__|__=~/[0-9\.]+/?_[1]<<__.to_f : _[2][__]?_[1]<<_[1].pop.send(__,_[1].pop):0}[0][0])

WTF?

This is an RPN calculator. Implemented in 138 characters of Ruby.

How?

Run it like such:

ruby calc.rb

And input your calculation, like:

1 3 +

And it will return:

4.0

Have fun!

Line-by-Line explanation.

Here's a freaking long explanation of how this code works. If you don't need to know, or just don't want to read this, you don't have to!

First off, here's the code split up a little with some whitespace:

p(
  (_=[gets.split,
   [],
   "+-**/"]
  )[0].map{|s|
    s = ~/[0-9\.]+/ ?
      _[1] << s.to_f :
      _[2][s] ?
      _[1] << _[1].pop.send(s.to_sym, _[1].pop) :
      0
  }[0][0])

It's still really cryptic. Maybe even more so. I'll go through it line, by line.

p(

The very first thing prints the result, once we get it.

(_=[gets.split,
	      [],
	      "+-**/"]

Next we want to initialize some variables. But separate variable names take up a lot of text. So instead we have a single variable: _, that is an array. The first element is our input (split by whitespace into an array), the second is an empty stack, the third is a string list of operators. Normally you'd think a list of operators would be an array, not a string, but we've got some fancy stuff going on here!

)[0].map{|s|

Next we take the first element of our fancy array (the input array) and go through each element, with s representing the current element. We don't do something like );_[0].map because when assigning to a variable, the value is returned when the variable is assigned. Allowing us to chain methods like this.

s = ~/[0-9\.]+/ ?

Next we test if the current input token is a number (actually a float, since it can contain a decimal point). And we use the fun unary operator that works like: condition ? do_if_true : do_if_false.

_[1] << s.to_f :

If it is a number, we push the string to the stack, after converting it to a float.

_[2][s] ?

And now we get some fun fancy stuff with that operator string. Here we're testing to see if the current operator token is a substring of our operator string. If it is, our current token is an operator. Otherwise it's not.

_[1] << _[1].pop.send(s.to_sym, _[1].pop) :

This is probably the craziest part of the code. In Ruby, you can dynamically call methods of course. And the standard infix methods (like 1 + 3) can be represented like any other method, that is 1.+(3). Here's a run down of the program so far with the input of:

1 3 +

  1. The 1 was pushed to the stack as a float: 1.0.

  2. The 3 was pushed to the stack as a float: 3.0.

  3. The + operator is reached, and so:

    1. The 3 is popped from the stack, and is going to be sent the method for adding to it.
    2. The current operator (+) is converted to a symbol so we can call it (all methods in ruby also have a symbol equivalent).
    3. We pop from the stack again and supply that as an argument to our method we're calling. So in our case the final method call looks like:
(3.0).+(1.0)

So we call this, and then push the result to the stack, so our stack now only has a single value in it: 4.0.

0

If nothing else works (that is, if our input isn't a number or an operator). Just return zero, do nothing. Basically the shortest code I could come up with for this, because with unary conditionals you can't have an empty else part of it.

}[0][0])

Then we get the first element of a crazy doubly-nested array we finally get so we just print out the final value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment