【问题标题】:How can I show that the Ruby `for` loop is in fact implemented using the `each` method?如何证明 Ruby `for` 循环实际上是使用 `each` 方法实现的?
【发布时间】:2017-02-11 21:10:34
【问题描述】:

Eloquent Ruby(第21页,第一版,第六版)一书中,作者(Russ Olsen)提倡使用each方法而不是@ 987654324@ 循环,这与我在其他地方读到的所有内容一致。

但是作者还继续说这样做的一个原因是for 循环实际上调用了each 方法,那么为什么不直接去掉中间人并使用each?所以我想知道这实际上是如何工作的。

为了进行调查,我确实在 github 上的 Ruby 存储库上进行了搜索,但发现很难确定我在哪里/如何看到这一点。

重申问题:

如何证明 Ruby for 循环实际上是使用 each 方法实现的?

【问题讨论】:

标签: ruby loops for-loop each


【解决方案1】:

如何显示 Ruby for 循环实际上是使用 each 方法实现的?

查看字节码。

ruby --dump insns -e 'for n in 1..10; puts n; end'

打印出来的

== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
== catch table
| catch type: break  st: 0002 ed: 0006 sp: 0000 cont: 0006
|------------------------------------------------------------------------
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 2] n          
0000 trace            1                                               (   1)
0002 putobject        1..0
0004 send             <callinfo!mid:each, argc:0, block:block in <compiled>>
0006 leave            
== disasm: <RubyVM::InstructionSequence:block in <compiled>@<compiled>>=
== catch table
| catch type: redo   st: 0006 ed: 0013 sp: 0000 cont: 0006
| catch type: next   st: 0006 ed: 0013 sp: 0000 cont: 0013
|------------------------------------------------------------------------
local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 2] ?<Arg>     
0000 getlocal_OP__WC__0 2                                             (   1)
0002 setlocal_OP__WC__1 2
0004 trace            256
0006 trace            1
0008 putself          
0009 getlocal_OP__WC__1 2
0011 opt_send_without_block <callinfo!mid:puts, argc:1, FCALL|ARGS_SIMPLE>
0013 trace            512
0015 leave            

如您所见,它调用each,在第一行0004 上有一个块。

【讨论】:

    【解决方案2】:

    for 表达式的语义在ISO Ruby Language Specification 中定义如下:

    §11.4.1.2.3 for 表达式

    语法

    • for-expression for for-variable in expression em> do-clause end
    • for-variable left-hand-side | multiple-left-hand-side时间>

    for-expression表达式不能是jump-expression

    语义

    for-expression 的求值方式如下:

    1. 评估表达式。让O 成为结果值。
    2. E 成为 primary-method-invocation 形式的 primary-expression [这里没有行终止符].each do | block-formal-argument-list | block-body end,其中 primary-expression 的值为 Oblock-formal-argument-listfor-variable,block-body 是 do-clausecompound-statement

      评估 E,但跳过 §11.2.2 的步骤 c。

    3. for-expression 的值是调用的结果值。

    好的,所以基本上这意味着

    for for_variable in expression
      do_clause
    end
    

    的评估与

    相同
    O = expression
    O.each do |for_variable|
      do_clause
    end
    

    啊哈!但是我们忘记了一些事情!有这个不祥的“跳过 §11.2.2 的步骤 c”。事物!那么,§11.2.2 的步骤 c 是什么?说什么?

    • 将一组空的局部变量绑定推送到⟦local-variable-bindings⟧。

    注意步骤 b

    • 将执行上下文设置为Eb

    没有被跳过。

    因此,for 循环获得了自己的执行上下文,它以当前执行上下文的副本开始,但它没有获得自己的一组局部变量绑定。 IOW:它有自己的动态执行上下文,但没有自己的词法范围。

    【讨论】:

      【解决方案3】:

      您可以通过编写实现每个类的类来展示它:

      # Demo that for calls each
      
      class ThreeOf
        def initialize(value)
          @value = value
        end
      
        def each(&block)
          puts "In Each"
          block.call(@value)
          block.call(@value)
          block.call(@value)
        end
      end
      

      然后创建一个实例并在for循环中使用它:

      collection = ThreeOf.new(99)
      
      for i in collection
        puts i
      end
      

      运行它,你会看到打印出来的消息,for“循环”会循环三遍。

      或者(更有趣)你可以修改内置的 Array 类:

      class Array
        alias_method :orig_each, :each
      
        def each(*args, &block)
          puts "Array Each called!"
          orig_each(*args, &block)
        end
      end
      
      puts "For loop with array"
      for i in [1,2,3]
        puts i
      end
      

      您将再次看到打印的消息。

      【讨论】:

      • 从 Ruby 2.0.0 开始,您还可以在 Array 前面添加一个模块,而不是给它的 each 起别名。
      • 非常聪明却又如此简单。感谢您花时间回答 Russ,非常感谢。
      猜你喜欢
      • 1970-01-01
      • 2019-11-15
      • 1970-01-01
      • 2019-06-20
      • 1970-01-01
      • 2015-09-21
      • 1970-01-01
      • 2012-01-23
      • 2020-02-04
      相关资源
      最近更新 更多