Эквивалентность между ifs и формами падежных операторов в Ruby

Используя гемы ruby_parser и Ruby2Ruby, я пишу код, который отслеживает, какие условия были оценены, каковы были их результаты и параметры. Чтобы сделать это как можно более простым, я иногда немного переписываю AST. Конечно, я могу сделать это, только если уверен, что результат работает точно так же, как и оригинал.

Правильно ли я утверждаю, что следующие три фрагмента Ruby эквивалентны по функции, если предположить, что тройные точки заменены действительным выражением Ruby? Я пропускаю какие-нибудь крайние случаи?

case var
  when foo 
    something
  when ... 
    another_thing
  else 
    something_else
end

if foo === var 
  something
elsif ... === var
  another_thing
else  
  something_else
end

case
  when foo === var 
    something
  when ... === var 
    another_thing
  else 
    something_else
end

2 ответа

Решение

Эти три фрагмента эквивалентны, если var идемпотентен, т.е. оценивает var Несколько раз имеет те же побочные эффекты, что и оценка его один раз.

Так что если var на самом деле это переменная, вы в безопасности, но помните, что это может быть произвольное выражение, в том числе сообщение, отправленное в побочный метод (например, puts).

Например

case puts('Hello')
when 1
when 2

это не то же самое, что

if 1 === puts('Hello')
elsif 2 === puts('Hello')

потому что в последнем случае "Hello" будет напечатан дважды.

Лучшим переводом может быть:

__randomly_generated_guaranteed_unique_local_variable_jhggfq45g345 = var

if foo === __randomly_generated_guaranteed_unique_local_variable_jhggfq45g345 
  something
elsif ... === __randomly_generated_guaranteed_unique_local_variable_jhggfq45g345
  another_thing
else  
  something_else
end

case
  when foo === __randomly_generated_guaranteed_unique_local_variable_jhggfq45g345
    something
  when ... === __randomly_generated_guaranteed_unique_local_variable_jhggfq45g345
    another_thing
  else 
    something_else
end

Да, три фрагмента Ruby эквивалентны по функциям.

Обновить:

В Ruby выражение case является неявным ===. Итак, эти 3(обрезанные) по сути одинаковы:

case var when foo, if foo === var & case when foo === var

Я приведу документацию несколько раз здесь.

===, == а также eql? производить разные результаты для объектов класса. Они переопределены в других классах, таких как String, Если бы Foo и бар были StringВы могли бы заменить тройные равные === утверждение с foo == var или же foo.eql? var

Тем не менее, они отличаются для наших обычных классов.

===: в случае Class === (или же Class.===), операция вернет true, если аргумент является экземпляром класса (или подкласса). За:

class A 
end
class B < A
end
b = B.new

A === b => правда, b === A => ложь (b.instance_of? A => ложь, b.instance_of? B => правда)

==: на уровне объекта, == возвращает true, только если obj и other являются одним и тем же объектом.b == b => правда, B.new == B.new => ложь

Для класса Object, === фактически так же, как вызов ==,Fixnum === 1 => правда, 1 === Fixnum => ложь

EQL?: eql? Метод возвращает true, если оба имеют одинаковое значение.1 == 1.0 => правда, 1.eql? == 1.0 => ложь

Другие вопросы по тегам