Подтвердить что ты не робот

Комбинационный метод, например, крана, но способный вернуть другое значение?

Я переживаю этап, пытаясь избежать временных переменных и чрезмерного использования условных условий, где я могу использовать более жидкий стиль кодирования. Мне очень понравилось использование #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 здесь.

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

4b9b3361

Ответ 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 возвращает другое значение.