【问题标题】:Do all? and any? guarantee short-circuit evaluation?都做吗?和任何?保证短路评估?
【发布时间】:2013-12-19 06:15:11
【问题描述】:

pryirb 中测试一些代码,我得到以下结果:

[1] pry(main)> a = [1, 3, 5, 7, 0]
=> [1, 3, 5, 7, 0]
[2] pry(main)> a.any? {|obj| p obj; 3 / obj > 1}
1
=> true
[3] pry(main)> a.all? {|obj| p obj; 3 / obj > 1}
1
3
=> false

[2][3] 中,我看到似乎有短路评估会尽快中止迭代,但这是保证行为吗?阅读文档没有提到这种行为。我意识到我可以改用inject,因为它会遍历所有内容,但我有兴趣了解官方的 Ruby 视图是什么。

【问题讨论】:

  • garanteed 是什么意思?在 [2] 中,它在找到任何一个时停止评估,即 > 然后 1。我看不出有什么理由让它更进一步。 [3] 也是如此,当它发现任何错误时它停止了。还有很多其他方法可以通过整句对象进行评估...
  • 我的意思是,由于数组中有一个零,如果我使用inject,我会得到一个ZeroDivisionError,但会是一个符合标准的Ruby实现(假设有一个规范要符合!)总是在尽可能早的时候停止,从不尝试除以零?
  • 我相信甚至没有定义any?all?从头到尾遍历数组。为了保存跨 ruby​​ 版本,您不能做出这样的假设。对于可能没问题的单个 ruby​​ 版本。
  • 我认为@tessi 提出了要点:没有要求任何返回 true 或 false 的方法以任何特定顺序遍历集合。

标签: ruby


【解决方案1】:

是的。

在 Ruby 标准的最后一个 draft 中,all? 是这样定义的:

  1. 在接收器上调用方法each
  2. 对于方法each 产生的每个元素X:
    1. 如果给出了块,则以 X 作为参数调用块。如果此调用返回一个虚假对象,return false
    2. 如果没有给出block,并且X是一个假对象,return false
  3. 返回 true

注意第 2 步中的 return 这个词。这保证了短路评估。 any? 的定义类似。但是,该标准仍是草案,我不知道哪些 Ruby 实现(如果有)旨在符合标准。

【讨论】:

  • “然而该标准仍是草案” – JIS 标准于 2011-03-22 由 JSA 发布:webdesk.jsa.or.jp/books/W11M0090/index/… 然后传递给 ISO,该标准于 2012-02-14 发布:iso.org/standard/59579.html JSA 随后发布了一个修订版,使其标准与 ISO 于 2013 年 12 月 20 日发布的版本保持一致。因此,JSA 发布的原始 JIS 标准比您的回答早了近三年发布。
  • “我不知道哪些 Ruby 实现(如果有的话)旨在符合标准。” – 唯一明确基于 ISO 规范的实现是 MRuby。该规范是以描述性的方式创建的,即通​​过描述实现实际在做什么。特别是,该规范的设计使得现有实现无需任何更改即可符合规范,这意味着在创建规范时(2009-2010)存在的任何主要实现都是自动兼容的,这包括 MRI、YARV、 Rubinius、JRuby、IronRuby、MagLev、MacRuby、Ruby.NET、XRuby、……
  • ……我可能忘记了一些。但是请注意,这显然只适用于 2009 年的状态。从那时起,实现可能已经发生了变化(例如,Rubinius 变得非常固执己见,并且在某些情况下,当他们不同意 Ruby 设计者做出的决定时会偏离标准 Ruby ),当然 Ruby 可能已经改变了(我想到了冻结的字符串文字)。在某些时候,我认为不可能同时兼容 ISO/JSA Ruby 和当前的 Ruby,并且实现选择当前的 Ruby。 (看起来更新 ISO Ruby 并没有真正的激励措施。)
【解决方案2】:

any? method 只是在Enumerable 上实现了'or' 逻辑功能。可以理解为语句:

y = x1 v x2 v x3 v ... v xn

all? methodEnumerable之上实现了'and'逻辑功能。也可以理解为语句:

y = x1 * x2 * x3 * ... * xn

由于ArrayEnumerable,它也包括那些方法。因此,对于any? 方法,true 的第一次出现(既不是nil 也不是false)结果用true 结果打破了枚举。在示例中,yielded 在数字 4 上变为 true,因此这些方法会中断执行并返回 true:

[1,2,3,4,5].any? {| x | puts x ; x > 3 }
# 1
# 2
# 3
# 4
# => true

您也可以将DeMorgan's rule 应用于函数any?,并使用all? 方法:

![1,2,3,4,5].all? {| x | puts x ; x <= 3 }
# 1
# 2
# 3
# 4
# => true

对于all? 方法,falsenil 结果的第一次出现执行类似的操作,即返回false。例如,yielded 在数字 3 上变为 false,因此这些方法会中断执行返回 false:

[1,2,3,4,5].all? {| x | puts x ; x < 3 }
# 1
# 2
# 3
# => false

DeMorgan's transformation一起使用any?方法:

![1,2,3,4,5].any? {| x | puts x ; x >= 3 }
# 1
# 2
# 3
# => false

【讨论】:

    【解决方案3】:

    我认为这里有些模棱两可。

    考虑以下几点:

    RUBY_VERSION
    => "2.3.7"
    

    屈服于块时:

    [1,2,3,4,5].all? {| x | puts x ; x < 3 }
    # 1
    # 2
    # 3
    # => false
    

    当不让步时:

    def a
      puts "in a"
      true
    end
    
    def b
      puts "in b" 
      false
    end
    
    def c
      puts "in c"
      true
    end
    
    [a,b,c].all?
    # in a
    # in b
    # in c
    # => false
    

    看起来像条件​​ #2:

    If block is not given, and X is a falseish object, return false.

    无效。

    【讨论】:

    • 是的,这就是我现在遇到的问题(尽管我使用的是 Ruby 2.6.6)。当没有给出块时,我的代码肯定 *NOT* 短路。
    猜你喜欢
    • 2013-10-18
    • 2011-02-11
    • 2011-08-07
    • 1970-01-01
    • 2012-12-16
    • 2015-11-14
    • 2017-01-21
    相关资源
    最近更新 更多