Оператор Ruby с несколькими переменными с использованием массива - PullRequest
3 голосов
/ 29 января 2011

Я хотел бы сравнить несколько переменных для оператора case, и в настоящее время я думаю, что лучший способ сделать это - переопределить оператор равенства (===) для Array Это лучший способ?

Вот пример использования:

def deposit_apr deposit,apr 
  # deposit: can be nil or 2 length Array of [nil or Float, String]  
  # apr: can be nil or Float     
  case [deposit,apr] 
    when [[Float,String],Float] 
      puts "#{deposit[0]} #{deposit[1]}, #{apr*100.0}% APR"
    when [[nil,String],Float] 
      puts "#{apr*100.0}% APR on deposits greater than 100 #{deposit[1]}"
    when [[Float,String],nil] 
      puts "#{deposit[0]} #{deposit[1]}"
    else 
      puts 'N/A' 
  end
end

Единственная проблема заключается в том, что оператор равенства массива не применяет регистр, равный элементам массива.

ruby-1.9.2-p0 > deposit_apr([656.00,'rupees'],0.065)
N/A

Будет, если я переопределю, но я не уверен, что бы я сломал, если бы сделал:

class Array
  def ===(other)
    result = true
    self.zip(other) {|bp,ap| result &&= bp === ap}
    result
  end
end

Теперь все работает:

ruby-1.9.2-p0 > deposit_apr([656.00,'rupees'],0.065)
656.0 rupees, 6.5% APR

Я что-то упустил?

Ответы [ 3 ]

8 голосов
/ 08 октября 2015

Я нашел этот вопрос, потому что я искал оператор case для нескольких переменных, но, пройдя через следующее, пришел к выводу, что необходимость сравнения нескольких переменных может указывать на необходимость другого подхода.(Я вернулся к своему собственному коду с этим выводом и обнаружил, что даже Hash помогает мне писать код, который легче понять.)

В Gems сегодня используется «без патчей обезьян» в качестве продажипункт .Переопределение оператора, вероятно, не правильный подход.Исправления для обезьян отлично подходят для экспериментов, но слишком легко ошибиться.

Кроме того, существует множество проверок типов.На языке, который разработан для Duck Typing, это ясно указывает на необходимость другого подхода.Например, что произойдет, если я передам целочисленные значения вместо числа с плавающей запятой?Мы получили бы «N / A», хотя это вряд ли то, что мы ищем.

Вы заметите, что пример, приведенный в вопросе, трудно прочитать.Мы должны быть в состоянии найти способ более четко представить эту логику читателю (и автору, когда они снова через несколько месяцев снова пересматривают код и должны ломать голову над тем, что происходит).

Инаконец, поскольку существует несколько чисел со связанной логикой, создается впечатление, что по крайней мере один класс значения -типа класса (Deposit) хочет записаться.

Для чистоты яПредположим, что nil APR можно считать 0,0% годовых.

class Deposit
  def initialize(amount, unit='USD', options={})
    @amount = amount.to_f # `nil` => 0.0
    @unit   = unit.to_s   # Example assumes unit is always present
    @apr    = options.fetch(:apr, 0.0).to_f # `apr: nil` => 0.0
  end
end

Как только у нас будет наш объект Deposit, мы сможем реализовать логику печати, не требуя операторов case.

class Deposit

  # ... lines omitted

  def to_s
    string = "#{@amount} #{@unit}"
    string << ", #{@apr * 100.0}% APR" if @apr > 0.0
    string
  end
end

d = Deposit.new(656.00, 'rupees', apr: 0.065)
d.to_s
# => "656.0 rupees, 6.5% APR"

e = Deposit.new(100, 'USD', apr: nil)
e.to_s
# => "100.0 USD"

f = Deposit.new(100, 'USD')
f.to_s
# => "100.0 USD"

Заключение : Если вы сравниваете несколько переменных в выражении case, используйте это как запах, чтобы предложить более глубокую проблему проектирования.Множественная переменная case может указывать на наличие объекта, который нужно создать.

2 голосов
/ 29 января 2011

Если вы беспокоитесь о том, чтобы что-то сломать, изменив поведение Array, и, конечно, это разумное беспокойство, то просто поместите ваш пересмотренный оператор в подкласс Array.

0 голосов
/ 29 января 2011

это определенно не лучший способ. еще больше - вам не следует переопределять методы стандартных классов, поскольку от этого может зависеть функциональность ядра, - тогда получайте удовольствие от отладки.

стиль защиты хорош (с большим количеством проверок типов и так далее), но обычно он ухудшает производительность и читабельность.

если вы знаете, что в этот метод вы не передадите ничего, кроме набора чисел с плавающей запятой и строк - зачем вам все эти проверки?

ИМО использует перехват исключений и исправляет источник проблемы, не пытайтесь решить проблему где-то посередине

...