像tap一样的组合方法,但能够返回不同的值吗?

时间:2011-10-24 16:21:09

标签: ruby theory combinators

我正在经历一个试图避免临时变量和过度使用条件的阶段,我可以使用更流畅的编码风格。我非常喜欢在我想要获得我需要返回的值的地方使用#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's 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这样的几乎的愿望。

我可以在此处遵循其他模式。我意识到这对于某些人来说只是荒谬的糖,我应该转移到更有趣的问题,但我正在努力学习写一个更具功能性的风格,所以我只是好奇长期的rubyists确定了什么要成为解决这种情况的好方法。这些例子非常简单。

5 个答案:

答案 0 :(得分:15)

定义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

答案 1 :(得分:7)

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会返回另一个值。

ruby​​ 2.5有yield_self你想要的。 https://stackoverflow.com/a/47890832/683157

答案 2 :(得分:3)

我在Facets gem中找到了一个可能正是您所寻找的方法: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 :(得分:1)

我需要做这样的事情,我喜欢tokland的回答,但是我并不想为我正在编写的小脚本污染Object。相反,我在数组上使用了tap

[something_complicated].tap { |s| s[0] = new_cool_thing)}.first

答案 4 :(得分:0)

instance_eval可能被用于此目的

"this".instance_eval { |test| test + " works" }

从2.5开始,可以使用yield_self

"easy".yield_self{ |a| a + " peasy" }

了解更多:

https://ruby-doc.org/core-1.9.3/BasicObject.html#method-i-instance_eval

https://ruby-doc.org/core-2.5.0/Object.html#method-i-yield_self