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

Viv*_*ngh 16 ruby ruby-on-rails

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

是什么区别yield_self,yield(self)和现有的Object#tap方法?

mik*_*kej 36

tap和之间的区别在于yield_self两种方法中的每种方法返回的内容.

Object#tap产生自我到块然后返回自我.Kernel#yield_self产生self到块然后返回的结果.

以下是一些有用的示例:

龙头

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

def my_method
  result = get_some_result
  call_some_method_with result
  result
end
Run Code Online (Sandbox Code Playgroud)

可以写成:

def my_method
  get_some_result.tap do |result|
    call_some_method_with result
  end
end
Run Code Online (Sandbox Code Playgroud)

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

some_object = SomeClass.new.tap do |obj|
  obj.field1 = some_value
  obj.field2 = other_value
end   
Run Code Online (Sandbox Code Playgroud)

yield_self和yield(self)

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

MichałŁomnicki撰写的这篇博文有一些有用的例子.例如这段代码:

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

可以改写为:

"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
Run Code Online (Sandbox Code Playgroud)

这有助于清楚地将嵌套调用用于某些数据的一系列转换.其他编程语言中也存在类似的特征.Elixir管道操作员是个好看的人,


SRa*_*ack 7

这里有一个很好的总结:Ruby 2.5添加了yield_self.

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

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

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

irb> "Hello".yield_self { |str| str + " World" }
  => "Hello World"
Run Code Online (Sandbox Code Playgroud)

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

如果没有方法参数,try的行为类似于yield_self.它将屈服于给定的块,除非接收器为nil并返回块中最后一个语句的输出.

irb> "Hello".try { |str| str + " World" }
  => "Hello World"
Run Code Online (Sandbox Code Playgroud)

要注意的几点差异,尝试不是Ruby的一部分,而是Rails.另外尝试的主要目的是防止nil因此如果接收器为零则不执行块.

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

irb> nil.try { |obj| "Hello World" }
  => nil
Run Code Online (Sandbox Code Playgroud)

点击怎么样?

tap也类似于yield_self.它是Ruby本身的一部分.唯一的区别是返回的值.tap返回接收器本身,而yield_self返回块的输出.

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

irb> "Hello".tap { |str| str + " World" }
  => "Hello"
Run Code Online (Sandbox Code Playgroud)

总的来说,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"
Run Code Online (Sandbox Code Playgroud)

yield_self是Kernel的一部分,因此它可用于所有对象.

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

  • 该参考文献很有帮助,但评论中的链接就足够且适当了。 (2认同)
  • 我想要留下来.@SRack:你[可以将这个答案转换成社区维基](https://meta.stackexchange.com/questions/11740/what-are-community-wiki-posts),这样你就不会受益于upvotes关于你没有产生的内容:) (2认同)