Skip to content

Instantly share code, notes, and snippets.

@ejallday
Created April 3, 2013 02:24
Show Gist options
  • Select an option

  • Save ejallday/5297958 to your computer and use it in GitHub Desktop.

Select an option

Save ejallday/5297958 to your computer and use it in GitHub Desktop.
This is my version of the RPN Calculator after lots of googling. This is the full annotated version with my final at the bottom. I'm going to go through my entire thought process and things I struggled with. I was only able to come up with my final solution after seeing someone else's that was similar... But I learned a new trick in the process:…
# Before I started this exercise I read the wikipedia on reverse Polish notation. It made
# sense to me for the most part, but then I discovered their chart about stacks which is a
# total GEM! However, last night I read ericatdbc.tumblr.com 's blog about the importance of
# understanding the stack data structure by the end of week one. So I did some additional
# research but still didn't fully understand how it worked. At this point, I thought I
# should give it a try anyways.
# Step 1: After reading the wiki and the blog I knew I needed to get my input string into
# an array. Using one of the example strings on Socrates I defined the string as a variable and
# using the class String #split method I was able to achieve that. You will also see that
# along the way I constantly check my outputs to see where I am using Ruby Markers( # =>)
string = ('50 20 -')
elements = string.split # => ["50", "20", "-"]
# Step 2: At this point I spent about 30 minutes trying to figure out how to implement the
# sequence shown on wikipedia. I knew that I needed to push any element that was an integer
# to another array and pop the symbols (while forcing the first symbol upon the first two
# pushed numbers) So I created a blank one to help me get started.
helper = [] # => []
#Now that I have my "helper"...
# Step 3: I know now that I need a way to get my elements from the "elements" array into
# the helper array. After researching a bit more about #push, #pop, #map, #map! I learned
# that a) for my integer elements, #pop made no sense. #pop removes the last element from
# your array and changes the array. b) #map would allow me to change the elements if
# needed, and permanently if I used #map! but that didn't seem helpful either. I didn't
# need to change them, I needed to move them. Based on the wikipedia article I knew I
# simply needed to access each integer and put it in a new array until I could instruct it
# with either add, subtract or multiply to the following integer based on the first symbol
# (+, - or *). So ultimately I started playing with #push
# I did two practice runs...
helper.push(elements[0]) # => ["50"]
helper.push(elements[0]) # => ["50", "50"]
# After practicing I was reminded that #push does not change the original array and does
# not remove any "pushed" elements. So, at this point I know I will either have to iterate
# through each element in my elements array or find a way to remove the [0] index from
# elements after I've used it... So I need a loop or an iterator.
# Before moving on lets set helper back to []
helper = []
helper # => []
# Step 4: This is where things get a bit tricky. I could possibly use #while, #until which
# a had some experience with but not much or I could use #each or #times_do. If I try hard
# enough, I may be able to get any of these to work... But I know from the wiki that I
# only need to access each array index 1 time, so #each seemed like the easiest method. So
# I started to play with multiple element pushes.
elements.each do |elements|
helper.push(elements)
end
helper # => ["50", "20", "-"]
helper = [] # setting back to empty
# As shown above by my marker, using, the #each method did exactly what I needed it to, it
# pushed my elements from one array to another, one at a time. This may not seem like a
# huge revelation; it isn't. However, I find that by breaking these problems into the
# smallest incremental pieces keeps you moving forward and on the right path. Now that I
# know how to get my elements into my helper array, I need a way of telling the #each
# iterator to do something different every time it comes across a symbol, rather than an
# integer. At this point, my iterator has no regard for what its pushing, its just pushing
# everything to the helper array. I need a condition.
# Step 5: How do I tell my iterator to do something different than #push when it comes
# across a symbol? I thought about that for a while and I realized that before I could
# tell it what to do when it came across a symbol, I had to tell it what a symbol was. My
# first thought was regex. I thought if I could somehow say "its not an integer" that
# would be enough, but the more I played with the regex, the more I realized that its not
# just important to know that an element isnt an integer, I need to know what it is so I
# can tell the elements I have stored in my helper how to interact with each other in the
# next step.
# For this example I'm practicing with the only symbol "-" in my string so I figured I
# would start there by saying, if you come across "-" in iteration... do...
elements.each do |elements|
if elements != "-"
helper.push(elements)
else
helper[0] - helper[1]
end
helper # => ["50"], ["50", "20"]
end
# Step 6: As you can see from the ruby markers above, I was able to push my first two elements to
# my helpers array, but when it came to subtracting one from the other I got a :String
# (NoMethodError). Ah!! I had forgotten that when I tried to subtract the [1] index from the
# [0] index that they were still strings. One quick edit and I add the #to_i to my push
elements.each do |elements|
if elements != "-"
helper.push(elements.to_i)
else
helper[0] - helper[1]
end
helper # => [50], [50, 20], [50, 20]
end
# Step 7: When I got the above result, for a minute I was puzzled when [50, 20] re-appeared in the
# 3rd iteration. I was sort of expecting to see [30]. But then I realized that I didn't
# tell the helper array to remove index [0] and index [1] and subtract one from the
# other... AND I didn't tell it where to store the result OR where to put it.
# Step 7: I need another variable. At the same time it dawned on me, if I have to remove
# the integers stored in my helper array and subtract one from the other I can't use
# indexes if they are gone... So they need to be removed and to be assigned variables too.
# For this I knew #pop was perfect. I also took the opportunity to rearrange and organize a
# bit.
elements.each do |elements|
if elements == "-"
x = helper.pop # => 20
y = helper.pop # => 50
z = x - y
helper.push z
else
helper.push(elements.to_i)
end
helper # => [50], [50, 20], [-30]
end
# Step 8: Alas!! Wait... [-30] ?!?! By looking at my Ruby Markers I can see that when I removed my
# integers from my helper array using the #pop method, it removed them one at a time and
# set them equal to variables, but because #pop removes the last element in an array, I
# realized I needed to swap my 'x' and 'y' variables.
elements.each do |elements| # !> shadowing outer local variable - elements
if elements == "-"
y = helper.pop # => 20
x = helper.pop # => 50
z = x - y
helper.push z
else
helper.push(elements.to_i)
end
helper # => [50], [50, 20], [30]
end
# Step 9: Now that I have tackled one example, I know I need to make my method more robust
# so that it can handle any string including stings that have '*' and '+'. Initially my
# first thought was to use an 'if' and two 'or's but then I would be repeating myself. I
# considered making each symbol its own variable when I stumbled across something I had
# never seen before: CONSTANTS.
# Step 10: a CONSTANT variable is something that once defined in a class cannot be changed
# and when written inside of a class they function like an instance (@) variable. Usually
# they are used for mathematical constants that never change, but in this case, we wont be
# changing any of our symbols so I gave it a whirl.
# (bringing the entire class together in this section. I apologize for not adhering
# to DRY)
class RPNCalculator
SYMBOLS = [ "+", "-", "*" ]
def evaluate(string)
elements = string.split
helper = []
elements.each do |elements|
if SYMBOLS.include?(elements)
y = helper.pop
x = helper.pop
if elements == "+"
z = x + y
elsif elements == "-"
z = x - y
else elemnts == "*"
z = x * y
end
helper.push z
else
helper.push elements.to_i
end
end
return helper [0]
end
end
# Note: from this point down, I had to do my testing with my command line, as I am still
# new to ruby marker. I continued to test each step as I implemented with Text Mate.
# Step 10 (continued): As you can see above once I defined my class (which the exercise
# told me I would eventually have to do) I defined my SYMBOLS as constants.
# Step 11: Prior to this step I was using "if elements != "-" || if elements == "-"" to
# tell my iteration to "do something different". In this step, I used the definition of
# SYMBOLS to my advantage and used #include? so that I wouldnt have to write an "if" and
# two "or"s to capture all three symbols.
# Step 12: Once I added in additional functionality for more than one symbol, I needed to
# tell my method what to do with the "+" and the "*" symbols so I added them to my if
# statement.
# Step 13: The final step prior to refactoring was to have my method eventuall return my
# result. I did this by simply adding "return helper[0]" as that is all that should be
# left once interation is complete on any valid string.
# Step 14: As a refactoring novice... I didn't see a whole lot that I could improve on.
# However, the easiest thing to see was that I could replace all my "if" statements with
# a "case" which I think is a bit cleaner. I know there must be a way to condense these 7
# lines to 1 or 2 but I'm still working on that.
class RPNCalculator
SYMBOLS = [ "+", "-", "*" ]
def evaluate(string)
elements = string.split
helper = []
elements.each do |elements|
if SYMBOLS.include?(elements)
y = helper.pop
x = helper.pop
case elements
when "+"
z = x + y
when "-"
z = x - y
when "*"
z = x * y
end
helper.push z
else
helper.push elements.to_i
end
end
return helper [0]
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment