有吗?找到匹配项时从循环中断?

时间:2018-07-25 10:45:01

标签: ruby

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;
}

4 个答案:

答案 0 :(得分:4)

该术语是“短路”,是的,any?是这样做的。找到匹配项后,就再也看不到了。

答案 1 :(得分:4)

是的,它确实打破了循环。无需深入研究代码即可检查:

[1,2,3].any? { |e| puts "Checking #{e}"; e == 2 }
# Checking 1
# Checking 2
#⇒ true

答案 2 :(得分:2)

  

any?在找到匹配项时会从循环中中断吗?

The documentation is unclear about that

  

如果块曾经返回truefalse以外的值,则该方法返回nil

注意:它不会说“ 时,该块一旦返回falsenil以外的值”或“ 该块曾经返回falsenil“以外的值。

这可以用任何一种方式解释,也可以解释为根本不做任何保证。如果您使用本文档,则可以都不保证它会短路,也不能保证它会不会短路-电路。

通常来说,这是API规范的典型做法:做出最少的保证,使API实现者在如何实现API方面拥有最大的自由度。

我们还有其他地方可以看:the ISO Ruby Programming Language Specification粗体重点):

  

15.3.2.2.2 Enumerable#any?

any?(&block)
     

可见性:公开

     

行为

     

a)在接收方上调用方法each

     

b)对于each产生的每个元素 X

     
      
  1. 如果给出了 block ,请以 X 作为参数调用 block 。   如果此调用导致产生 true 对象,请返回true
  2.   

如您所见,它再次只显示“如果”,而不是“何时”或“尽快”。可以用两种方式解释该句子:“作为方法的结果返回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 implementationthis一样实现短路:

if (predicate.Yield(item, out blockResult)) {
    result = blockResult;
    return selfBlock.PropagateFlow(predicate, blockResult);
}

JRuby's implementation仍然涉及更多一点,但是您可以看到,一旦遇到真实的块值,它就会通过throwing a SPECIAL_JUMP exceptioncatching 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次。