khpeek khpeek - 5 months ago 12
Ruby Question

How to pass a mathematical operation (such as +, -, *, /) as an argument to a method

I'm writing a Reverse Polish Notation (RPN) calculator which, until now, looks like this:

class RPNCalculator
attr_accessor :numbers, :value
def initialize
@numbers = []
@value = nil
end

def push(number)
@numbers.push(number)
end

def plus
if value.nil?
number1=@numbers.pop
number2=@numbers.pop
@value = number1 + number2
else
@value += @numbers.pop
end
end

# minus_proc = Proc.new {|a,b| a - b}

def minus
number1=@numbers.pop
number2=@numbers.pop
@value = number2 - number1
# operate_on_two_numbers(minus_proc)
end

def operate_on_two_numbers(&prc)
number1=@numbers.pop
number2=@numbers.pop
@value = prc.call(number1,number2)
end

end


For example, the following commands

calculator=RPNCalculator.new
calculator.push(5)
calculator.push(8)
calculator.plus
p calculator.value


produce the number "13".

This is how it's supposed to work, but there seems to be a lot of repetition in the code. For example, the "plus" and "minus" functions are largely the same; only a "+" and "-" inside the code differs.

I'd like to try to define a (private) method which accepts as an argument an operation which operates on two numbers, such as "+", "-", "*", or "/". It would be nice to use 'shortcut notation' like the shortcut
.inject(:+)
.

I've given it a try with the commented-out lines and the method
operate_on_two_numbers
, but when I comment in the lines I get an error "undefined local variable or method 'minus_proc'".

Answer

You could use any arithmetic operator here in the same manner

def operate_on_two_numbers(a, b, op)
  a.send(op, b)
end

operate_on_two_numbers(1, 2, :+)
#=> 3
operate_on_two_numbers(1, 2, :-)
#=> -1

Here's what your full code could look like

class RPNCalculator
  attr_accessor :numbers, :value
  def initialize
    @numbers = []
    @value = nil
  end

  def push(number)
    @numbers.push(number)
  end

  def plus
    operate_on_two_numbers(:+)
  end

  def minus
    operate_on_two_numbers(:-)
  end

  private

  def operate_on_two_numbers(op)
    number1=@numbers.pop
    number2=@numbers.pop
    @value = number1.send(op, number2)
  end
end

calculator=RPNCalculator.new
#=> #<RPNCalculator:0x00000002efe460 @numbers=[], @value=nil>
calculator.push(5)
#=> [5]
calculator.push(8)
#=> [5, 8]
calculator.plus
#=> 13
calculator.value
#=> 13
Comments