Комбинационный метод, например, крана, но способный вернуть другое значение?
Я переживаю этап, пытаясь избежать временных переменных и чрезмерного использования условных условий, где я могу использовать более жидкий стиль кодирования. Мне очень понравилось использование #tap
в тех местах, где я хочу получить значение, которое мне нужно вернуть, но сделайте что-нибудь с ним, прежде чем я его верну.
def fluid_method
something_complicated(a, b, c).tap do |obj|
obj.update(:x => y)
end
end
Vs. процедурный:
def non_fluid_method
obj = something_complicated(a, b, c)
obj.update(:x => y)
obj # <= I don't like this, if it avoidable
end
Очевидно, что приведенные выше примеры просты, но, тем не менее, это довольно распространенный стиль кодирования в рубиновом сообществе. Я иногда использую #inject
для передачи объекта через серию фильтров:
things.inject(whatever) do |obj, thing|
thing.filter(obj)
end
Vs. процедурный:
obj = whatever
things.each do |thing|
obj = thing.filter(obj)
end
obj
Теперь я сталкиваюсь с повторным использованием такого состояния, как следующее, и ищет более гибкий подход к его обработке:
def not_nice_method
obj = something_complex(a, b, c)
if a_predicate_check?
obj.one_more_method_call
else
obj
end
end
(слегка) более чистое решение состоит в том, чтобы избежать временной переменной за счет дублирования:
def not_nice_method
if a_predicate_check?
something_complex(a, b, c).one_more_method_call
else
something_complex(a, b, c)
end
end
Я не могу не чувствовать желания использовать что-то почти как #tap
здесь.
Какие еще шаблоны я могу использовать здесь. Я понимаю, что это всего лишь бессмысленный сахар для некоторых людей, и что я должен просто перейти на более интересные проблемы, но я пытаюсь научиться писать в более функциональном стиле, поэтому мне просто интересно, что определили долгосрочные рубисты чтобы быть хорошим способом решения подобных ситуаций. Эти примеры очень упрощены.
Ответы
Ответ 1
Определить Object#as
:
class Object
def as
yield self
end
end
И теперь вы можете написать:
def not_sure_this_is_nice_enough_method1
something_complex(a, b, c).as do |obj|
a_predicate_check? ? obj.one_more_method_call : obj
end
end
Ответ 2
Я нашел метод в гране Граней, который мог бы быть тем, что вы искали: Kernel#ergo
Итак, ваш оригинальный метод:
def not_nice_method
obj = something_complex(a, b, c)
if a_predicate_check?
obj.one_more_method_call
else
obj
end
end
может выглядеть примерно так:
require 'facets/kernel/ergo'
def nice_method
something_complex(a, b, c).ergo do |_|
a_predicate_check? ? _.one_more_method_call : _
end
end
Ответ 3
Мне нужно было сделать что-то вроде этого, и мне нравится tokland ответ, но я не хотел загрязнять Object для небольшого script, который я писал. Вместо этого я использовал tap
для массива:
[something_complicated].tap { |s| s[0] = new_cool_thing)}.first
Ответ 4
def best_nice_method
something_complex(a, b, c).tap |obj|
break obj.one_more_method_call if a_predicate_check?
end
end
Магия break
в tap
возвращает другое значение.