简体   繁体   中英

Ruby - how to raise same error for multiple methods without writing it multiple times?

Lets say I create a Calculator class that works by manipulating elements in an array - within this class I define several methods: add, subtract, multiply, divide. I want each method to raise the same error if there exist only 1 or fewer elements in the array, something like:

class Calculator
# ...
def add
  if @array.length < 2
    raise 'Not Enough Elements'
  else
    @array << @array.pop + @array.pop
  end
end
# ...
end

I could write a condition to raise the error into each method, but that seems very tedious and un-Ruby. Would there be a way to apply the raised error to all the methods that would need it that would save all that typing?

One of the options would be moving the length checking logic into it's own method and using it where needed:

class Calculator
  def add
    check_array_length
    # rest of the method
  end

  private

  def check_array_length
    raise 'Not Enough Elements' if @array.length < 2
  end
end

If you are setting @array in the initialize method, you could raise on the early stage, saying that you can't proceed because of too less elements in the @array :

class Calculator
  def initialize(array)
    raise 'Not Enough Elements' if array.length < 2

    @array = array
  end
end

Here's a possible structure :

module Validator
  [:add, :substract, :multiply, :divide].each do |method|
    define_method(method) do
      validate_array_length(2)
      super()
    end
  end

  private

  def validate_array_length(min,max=min)
    raise 'Not Enough Elements' if @array.length < min
    raise 'Too Many Elements' if @array.length > max
  end
end

class Calculator
  prepend Validator
  def initialize(*values)
    @array = values
  end

  def add
    @array << @array.pop + @array.pop
  end

  # def substract ....
end

c = Calculator.new(3,2)
c.add
c.add
# => calculator.rb:12:in `validate_array_length': Not Enough Elements (RuntimeError)
class Calculator
  def initialize(arr)
    @arr = arr
  end

  def add;      binary(:+);    end
  def subtract; binary(:-);    end
  def multiply; binary(:*);    end
  def divide;   binary(:/);    end
  def power;    binary(:**);   end
  def modulo;   binary(:%);    end
  # ... (others)

  def negate;   unary(:-@);    end
  def odd?;     unary(:odd?);  end
  def even?;    unary(:even?); end
  def to_f;     unary(:to_f);  end
  # ... (others)

  private

  def binary(op)
    raise ArgumentError, 'Too few elements' if @arr.length < 2
    @arr.pop.send(op, @arr.pop)
  end

  def unary(op)
    raise ArgumentError, 'Too few elements' if @arr.length.zero?
    @arr.pop.send(op)
  end
end

#                      add  neg   mod   pow   div  mult     sub     add 
calc = Calculator.new [  1,   5,  2,3,  4,5,  6,7,  8,9,  10,11,  12,13]
  #=> #<Calculator:0x007fa192030968 @arr=[1, 5, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]> 

calc.add       #=> 25  (13+12) 
calc.subtract  #=> 1   (11-10) 
calc.multiply  #=> 72  (8*9)   
calc.divide    #=> 1   (7/6)
calc.power     #=> 625 (5**4)
calc.modulo    #=> 1   (3%2)
calc.negate    #=> -5  (5)
calc.add       #=> ArgumentError: Too few elements

It appears that you are constructing an RPN calculator. If so, you probably want to push the result of each calculation back onto the stack. For binary operators you could change the method binary as follows:

  def binary(op)
    raise ArgumentError, 'Too few elements' if @arr.length < 2
    @arr << @arr.pop.send(op, @arr.pop)
    @arr[-1]
  end

@arr[-1] , the result of the calculation, is the return value. The modification of unary is similar.

You may wish to add some stack manipulation methods, such as

def pop
  @arr.pop
end

def push(n)
  @arr << n
end

def swap
  @arr[-1], @arr[-2] = @arr[-2], @arr[-1]
end

def rotate
  @arr.rotate
end

Lastly, you might find it it clearer to make the beginning (rather than end) of @arr the top of the stack, in which you would use unshift/shift rather than push/pop .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM