Ruby:块内的产量

Bru*_*uce 5 ruby yield return

这是扫描的示例:

"abcdeabcabc".scan("a")
Run Code Online (Sandbox Code Playgroud)

所以它返回一个由 3 个 a 组成的数组。另一个扫描示例:

"abcdeabcabc".scan("a") {|x| puts x}
Run Code Online (Sandbox Code Playgroud)

它只是输出每个“a”,但仍然输出一个数组,这一次它实际上是它返回的原始字符串。

因此,从文档和上面的行为来看,扫描要么返回一个数组(没有给出块),要么返回原始字符串,在此之前会发生一些副作用。关键是这两种情况都会返回一些东西。

那么如果我在块内放置一个“yield”会发生什么?将返回什么?或者,没有?返回值的类型是什么?

"abcdeabcabc".scan("a") {|x| yield x}
Run Code Online (Sandbox Code Playgroud)

上面的代码不起作用,因为 Ruby 抱怨没有给出任何块。这对我来说有一定道理。但如果它是类方法的一部分,例如,自我实现的“each”,则以下内容有效:

class Test
  def my_each
    "abcdeabcabc".scan("a") {|x| yield x}
  end
end
# => :my_each

t = Test.new
# => #<Test:0x007ff00a8d79b0>

t.my_each {|x| puts "so this is #{x}"}
# it works. Outpus 3 a's then return the original string.
Run Code Online (Sandbox Code Playgroud)

那么,Test类的my_each方法的返回值是什么呢?这是收益率列表还是其他什么?但正如之前讨论的 "abcdeabcabc".scan("a") {|x| Ruby 会抱怨yield x} 段,直到给出一个块为止。将 my_each 块赋予 my_each 实现内部的段,内部发生了什么?

D-s*_*ide 4

该块的传递方式与该函数的参数类似。这可以明确指定,如下所示:

class Test
  def my_each(&block)
    "abcdeabcabc".scan("a") do |x|
      puts "!!! block"
      yield x
      # Could be replaced with: block.call(x)
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

从技术上讲,它是完全相同的(puts放在那里是为了澄清),它的存在不会像通常对参数所做的那样进行检查。如果你忘记给它一个块,函数将在yield它必须以完全相同的方式执行的第一个地方停止LocalJumpError(至少,这是我在 Rubinius 上得到的)。但是,请在发生之前注意控制台中的“!!! block”。

它这样工作是有原因的。您可以使用 来检查您的函数是否被赋予了一个块(如果它是如上所述明确指定的)if block,然后跳过yields。Rails 的助手就是一个很好的例子content_tag。该助手的调用可以是块嵌套的。一个简单的例子:

content_tag :div do
  content_tag :div
end
Run Code Online (Sandbox Code Playgroud)

...产生如下输出:

<div>
  <div></div>
</div>
Run Code Online (Sandbox Code Playgroud)

因此,该块在方法的“顶部”(就调用堆栈而言)执行。每次 a 发生时都会调用它yield作为块上的某种函数调用。它不会在任何地方累积以供之后执行该块。

更新:

Enumerator许多 es 返回的结果each是由许多迭代器显式构造的,以保存应该发生的情况的上下文。

它可以像这样实现my_each

<div>
  <div></div>
</div>
Run Code Online (Sandbox Code Playgroud)