any?
在找到匹配项时会从循环中中断吗?
以下是any?
source code,但我不理解。
static VALUE
enum_any(VALUE obj)
{
VALUE result = Qfalse;
rb_block_call(obj, id_each, 0, 0, ENUMFUNC(any), (VALUE)&result);
return result;
}
答案 0 :(得分:4)
该术语是“短路”,是的,any?
是这样做的。找到匹配项后,就再也看不到了。
答案 1 :(得分:4)
是的,它确实打破了循环。无需深入研究c代码即可检查:
[1,2,3].any? { |e| puts "Checking #{e}"; e == 2 }
# Checking 1
# Checking 2
#⇒ true
答案 2 :(得分:2)
any?
在找到匹配项时会从循环中中断吗?
The documentation is unclear about that:
如果块曾经返回
true
或false
以外的值,则该方法返回nil
。
注意:它不会说“ 当时,该块一旦返回false
或nil
以外的值”或“ 该块曾经返回false
或nil
“以外的值。
这可以用任何一种方式解释,也可以解释为根本不做任何保证。如果您使用本文档,则可以都不保证它会会短路,也不能保证它会不会短路-电路。
通常来说,这是API规范的典型做法:做出最少的保证,使API实现者在如何实现API方面拥有最大的自由度。
我们还有其他地方可以看:the ISO Ruby Programming Language Specification(粗体重点):
15.3.2.2.2
Enumerable#any?
any?(&block)
可见性:公开
行为:
a)在接收方上调用方法
each
b)对于
each
产生的每个元素 X
- 如果给出了 block ,请以 X 作为参数调用 block 。 如果此调用导致产生 true 对象,请返回
true
如您所见,它再次只显示“如果”,而不是“何时”或“尽快”。可以用两种方式解释该句子:“作为方法的结果返回true
”(没有指示调用该块的频率,仅指示该方法最后将返回true
)或“在遇到求值为真值的块的调用时返回true
。
尝试#3:The Ruby Spec:
it "stops iterating once tähe return value is determined" do
所以,是的,我们确实可以依靠以下事实:仅在遇到第一个真实值之前才对块进行评估。
以下是
any?
source code,但我不理解。
注意:通过查看源代码,可以不确定Ruby中的行为。您只能确定Ruby特定实现的特定版本中的行为。不同的实现可能会有不同的行为(例如,在YARV中,Ruby线程不能同时运行,而在JRuby中则可以)。即使相同实现的不同版本也会表现不同。
仅通过查看单个实现的单个版本来对编程语言的行为进行假设通常不是一个好主意。
但是,如果您真的想看一下实现,并且完全了解这种方法的局限性,那么我建议您看一下Rubinius,Topaz,Opal,IronRuby或JRuby。 。 (在我看来)它们比YARV更有条理,更易于阅读。
例如this is the code for Enumerable#any?
in Rubinius:
def any?
if block_given?
each { |*element| return true if yield(*element) }
else
each { return true if Rubinius.single_block_arg }
end
false
end
这看起来很清晰易读,不是吗?
This is the definition in Topaz:
def any?(&block)
if block
self.each { |*e| return true if yield(*e) }
else
self.each_entry { |e| return true if e }
end
false
end
这看起来也很可读。
The soure in Opal稍微复杂一点,但只是稍微有点:
def any?(pattern = undefined, &block)
if `pattern !== undefined`
each do |*value|
comparable = `comparableForPattern(value)`
return true if pattern.public_send(:===, *comparable)
end
elsif block_given?
each do |*value|
if yield(*value)
return true
end
end
else
each do |*value|
if Opal.destructure(value)
return true
end
end
end
false
end
[请注意,可以使用重写`
方法将原义ECMAScript注入已编译的代码中的有趣用法。]
与Rubinius和Topaz版本相比,大多数增加的复杂性是由于Opal已经支持Ruby 2.5中引入的模式any?
的第三重载,而Rubinius和Topaz仅支持两者有一个块并且根本没有任何参数的超载。
IronRuby's implementation像this一样实现短路:
if (predicate.Yield(item, out blockResult)) {
result = blockResult;
return selfBlock.PropagateFlow(predicate, blockResult);
}
JRuby's implementation仍然涉及更多一点,但是您可以看到,一旦遇到真实的块值,它就会通过throw
ing a SPECIAL_JUMP
exception和catch
ing it to return true
跳出循环。
答案 3 :(得分:1)
是的,很容易证明:
irb(main):009:0> %w{ant bear cat}.any? {|word| puts "hello"; word.length >= 4}
hello
hello
=> true
它只打印了两次。如果没有破裂,它将打印3次。