Ruby 是否';任何?';找到匹配项时从循环中断?
Ruby 是否';任何?';找到匹配项时从循环中断?,ruby,c,Ruby,C,是否有?在找到匹配项时中断循环 下面是答案,但我不明白 static VALUE enum_any(VALUE obj) { VALUE result = Qfalse; rb_block_call(obj, id_each, 0, 0, ENUMFUNC(any), (VALUE)&result); return result; } 术语是“短路”,是的,any?会这样做。找到匹配项后,它不再进一步查看。是的,它确实打破了循环。无需深入研究代码即可检查: [
是否有?
在找到匹配项时中断循环
下面是答案,但我不明白
static VALUE
enum_any(VALUE obj)
{
VALUE result = Qfalse;
rb_block_call(obj, id_each, 0, 0, ENUMFUNC(any), (VALUE)&result);
return result;
}
术语是“短路”,是的,
any?
会这样做。找到匹配项后,它不再进一步查看。是的,它确实打破了循环。无需深入研究代码即可检查:
[1,2,3].any? { |e| puts "Checking #{e}"; e == 2 }
# Checking 1
# Checking 2
#⇒ true
是的,很容易证明:
irb(main):009:0> %w{ant bear cat}.any? {|word| puts "hello"; word.length >= 4}
hello
hello
=> true
它只印了两次。如果没有损坏,它将打印3次
是否有?
在找到匹配项时中断循环
:
如果块返回的值不是false
或nil
,则该方法返回true
注意:它没有说“当块返回的值不是false
或nil
”或“只要块返回的值不是false
或nil
”
这可以用任何一种方式来解释,也可以解释为根本不做任何保证。如果你按照这个文件去做,你既不能保证它会短路,也不能保证它不会短路
一般来说,这对于API规范来说是典型的:尽可能少的保证,让API实现者在如何实现API方面有最大的自由
还有其他地方我们可以看:(粗体强调我的):
15.3.2.2.2可枚举#任何?
能见度:公众
行为:
a) 在接收器上调用每个方法
b) 对于每个产生的每个元素X
如果给定block,则使用X作为参数调用block。
如果此调用产生一个纯对象,则返回true
正如你所看到的,它只是说“如果”,而不是“何时”或“尽快”。这句话可以用两种方式来解释:“作为方法的结果返回true
”(没有指示块被调用的频率,只是在结束时该方法将返回true
)或“当遇到计算结果为trueish值的块调用时返回true
”
试试看#3::
因此,是的,我们确实可以依赖这样一个事实,即在遇到第一个truthy值之前,只对块进行求值
下面是答案,但我不明白
static VALUE
enum_any(VALUE obj)
{
VALUE result = Qfalse;
rb_block_call(obj, id_each, 0, 0, ENUMFUNC(any), (VALUE)&result);
return result;
}
注意:通过查看源代码,您无法确定Ruby中的行为。您只能确定特定版本的特定Ruby实现中的某些行为。不同的实现可能表现不同(例如,在YARV中,Ruby线程不能同时运行,在JRuby中,它们可以同时运行)。即使是同一实现的不同版本,其行为也可能不同
仅仅通过查看单个实现的单个版本来假设编程语言的行为通常不是一个好主意
但是,如果您真的想了解一些实现,并且完全了解这种方法的局限性,那么我建议您了解Rubinius、Topaz、Opal、IronRuby或JRuby。在我看来,它们比YARV更有条理,更容易阅读
例如:
这看起来相当清晰易读,不是吗
:
这看起来也相当可读
稍微复杂一点,但只是稍微复杂一点:
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已经支持第三个重载any?
,采用Ruby 2.5中引入的模式,而Rubinius和Topaz只支持带块的两个重载,并且根本没有任何参数
实现短路,如:
更复杂一点,但你可以看到,一旦它遇到一个真实的块值,它就会跳出循环。是的,我正要发布一个演示片段:)@SergioTulentsev,我正要提到短路术语:)Ruby的答案证明它确实短路;但是如果您仍然想找到它在C中的位置,请查看ENUMFUNC(any)
的定义位置(查找DEFINE\u ENUMFUNCS(any)
),您应该在匹配上看到rb\u iter\u break()
。(顺便说一句,你的资料来源有点旧;这是MRI 2.5文件。)谢谢你回答的深度。下一次我将能够更进一步地回答我的问题。然而,大多数Ruby主义者都会同意,不管YARV做什么,它都是事实上的标准,其他所有的实现要么遵循YARV,要么在没有遵循的情况下明确说明。(我不认为ISO规范特别相关,因为它描述了截至2013年6月的一种死气沉沉的语言。)
def any?
if block_given?
each { |*element| return true if yield(*element) }
else
each { return true if Rubinius.single_block_arg }
end
false
end
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
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
if (predicate.Yield(item, out blockResult)) {
result = blockResult;
return selfBlock.PropagateFlow(predicate, blockResult);
}