这是扫描的示例:
"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 实现内部的段,内部发生了什么?
该块的传递方式与该函数的参数类似。这可以明确指定,如下所示:
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)