ruby中内核#yield_self,yield(self)和Object#tap之间的区别

时间:2017-12-19 15:51:16

标签: ruby-on-rails ruby

Ruby 2.5.0-rc1已经发布,并引入了一种新的Kernel#yield_self方法。

yield_selfyield(self)和现有的Object#tap方法之间有什么区别?

3 个答案:

答案 0 :(得分:27)

tapyield_self之间的区别在于两种方法中的每种方法返回的内容。

Object#tap为块生成self,然后返回self Kernel#yield_self为块生成self,然后返回块的结果

以下是一些有用的例子:

抽头

在方法结束时替换result行的需要:

def my_method
  result = get_some_result
  call_some_method_with result
  result
end

可以写成:

def my_method
  get_some_result.tap do |result|
    call_some_method_with result
  end
end

另一个例子是初始化某个对象,需要几个步骤:

some_object = SomeClass.new.tap do |obj|
  obj.field1 = some_value
  obj.field2 = other_value
end   

yield_self和yield(self)

如果在您自己的某个方法yield_self中使用,则与yield(self)具有相同的效果。但是,通过将其作为一种方法,这促进了方法链接作为嵌套函数调用的替代方法。

MichałŁomnicki的

This blog post有一些有用的例子。例如这段代码:

CSV.parse(File.read(File.expand_path("data.csv"), __dir__))
   .map { |row| row[1].to_i }
   .sum

可以改写为:

"data.csv"
  .yield_self { |name| File.expand_path(name, __dir__) }
  .yield_self { |path| File.read(path) }
  .yield_self { |body| CSV.parse(body) }
  .map        { |row|  row[1].to_i }
  .sum

这有助于明确嵌套调用用于某些数据的一系列转换。其他编程语言中也存在类似的特征。 pipe operator in Elixir是一个值得一看的好地方,

答案 1 :(得分:6)

这里有很好的总结:Ruby 2.5 added yield_self

快速更新:yield_self将在新版本的Ruby中有一个别名then,遵循社区的要求,以便更具可读性。

这是一篇非常好的,简明扼要的读物,下面引用后人的话。所有归功于原作者Vijay Kumar Agrawal:

  

Ruby 2.5添加了一个名为yield_self的新方法。它产生接收器   到给定的块并返回最后一个语句的输出   块。

irb> "Hello".yield_self { |str| str + " World" }
  => "Hello World"
     

与Rails中的尝试有什么不同?

     

没有方法参数试试   行为类似于yield_self。它会屈服于给定的块   除非接收器是nil并返回最后一个的输出   块中的陈述。

irb> "Hello".try { |str| str + " World" }
  => "Hello World"
     

值得注意的是,尝试不是Ruby的一部分,而是Rails。   另外尝试的主要目的是防止零,因此它没有   如果接收器为零,则执行该块。

irb> nil.yield_self { |obj| "Hello World" }
  => "Hello World"

irb> nil.try { |obj| "Hello World" }
  => nil
     

点击怎么办?

     

tap也类似于yield_self。它是Ruby的一部分   本身。唯一的区别是返回的值。点击返回   yield_self返回块的输出时接收器本身。

irb> "Hello".yield_self { |str| str + " World" }
  => "Hello World"

irb> "Hello".tap { |str| str + " World" }
  => "Hello"
     

总的来说,yield_self通过推广提高了代码的可读性   链接嵌套函数调用。这是两个例子   样式。

irb> add_greeting = -> (str) { "HELLO " + str }
irb> to_upper = -> (str) { str.upcase }

# with new `yield_self`
irb> "world".yield_self(&to_upper)
            .yield_self(&add_greeting)
  => "HELLO WORLD"

# nested function calls
irb> add_greeting.call(to_upper.call("world"))
  => "HELLO WORLD"
     

yield_self是内核的一部分,因此它可供所有人使用   对象。

请不要接受这个作为答案,因为这不是我自己的手工(如果有人有任何异议,我很乐意删除) - 但我发现这是一个非常好的阅读并认为它可能在某些时候帮助其他人。

答案 2 :(得分:3)

我想在mikej的答案中添加{strong> eachmaptapyield_self 之间的并行度的答案

有很多问题要求eachmap之间的差异。前者用于做一些副作用或破坏性的东西,然后返回接收器,这对于链接方法很方便。后者返回评估值代替接收器中的每个元素。

tapyield_self就是这样;区别在于它们是针对单个接收器对象而不是可枚举接收器中的元素完成的。所以他们的用例与我上面写的一致。前者用于做一些副作用或破坏性的东西,然后返回接收器,这对于链接方法很方便。后者返回评估值而不是接收者。