赞 | 0 |
VIP | 2 |
好人卡 | 27 |
积分 | 1 |
经验 | 26327 |
最后登录 | 2019-10-13 |
在线时间 | 953 小时 |
Lv1.梦旅人
- 梦石
- 0
- 星屑
- 110
- 在线时间
- 953 小时
- 注册时间
- 2007-4-25
- 帖子
- 805
|
本帖最后由 苏小脉 于 2011-1-21 01:32 编辑
Ruby 内置的循环结构只有 while 和 until 两种,for ... in ... 基本上可以看作是 Ruby 的语法糖,它最终会发送调用 each 方法的消息给 in ... 后面的对象。这其实也是为了减少模块的耦合而实现的——想要进行迭代的人不需要知道将要迭代的结构,就能实现迭代。所以,通过 Ruby 的鸭子类型,for ... in ... 或者直接调用 each 都不需要保证将要迭代的结构的运行时类型是可迭代的,只要它能响应 each 方法,它就是可以迭代的。所以在目的上 for ... in ... 和 each 没有区别。
这俩唯一的一个区别是局部变量的词法作用域的差异。for ... in ...,包括 while 和 until,都是语法糖,是不会产生新的作用域的,所以循环外部的变量可以在内部引用,而内部创建的局部变量对外也是可见的。each 则不同,由于其循环主体是在 Ruby 的块中,而块是拥有自己的作用域的,所以就有了差别。在块中创建的局部变量对外是不透明的:- [1].each do
- i = 2
- end
- p i # error
复制代码 除非在这个 each 的块的词法范围之外也出现了一个 i = 的赋值语句,这个判定是在词法分析时,一旦被 Ruby 词法分析器看见就知道是局部变量,并不一定要执行:
- if false
- i = 1
- end
- [1].each do
- i = 2
- end
- p i # OK
复制代码 效率上——对于,Ruby 1.8 得看具体解释器是如何处理的,理论上由于是直接在抽象文法树上求值,两者在文法分析时都会被剖析为一样的文法树节点,所以在执行时都没有区别,区别应该在于局部变量的创建,这个可以通过 1.9 YARV 编译 Ruby 代码后的指令来分析:这一段反汇编后是:
- |------------------------------------------------------------------------
- 0000 trace 1 ( 1)
- 0002 duparray [1]
- 0004 send :each, 0, block in <compiled>, 0, <ic:0>
- 0010 leave
- == disasm: <RubyVM::InstructionSequence:block in <compiled>@<compiled>>=
- == catch table
- | catch type: redo st: 0000 ed: 0001 sp: 0000 cont: 0000
- | catch type: next st: 0000 ed: 0001 sp: 0000 cont: 0001
- |------------------------------------------------------------------------
- local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1] s3)
- [ 2] i<Arg>
- 0000 putnil ( 2)
- 0001 leave
复制代码 而如果是:反汇编后:
- local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)
- [ 2] i
- 0000 trace 1 ( 1)
- 0002 duparray [1]
- 0004 send :each, 0, block in <compiled>, 0, <ic:0>
- 0010 leave
- == disasm: <RubyVM::InstructionSequence:block in <compiled>@<compiled>>=
- == catch table
- | catch type: redo st: 0005 ed: 0006 sp: 0000 cont: 0005
- | catch type: next st: 0005 ed: 0006 sp: 0000 cont: 0006
- |------------------------------------------------------------------------
- local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1] s3)
- [ 2] ?<Arg>
- 0000 getdynamic *, 0 ( 2)
- 0003 setlocal i ( 1)
- 0005 putnil ( 2)
- 0006 leave
复制代码 可见 Ruby 在处理 for 循环的循环局部变量 i 时,是需要额外的指令周期的。同时,由于它的生命周期比块内部处理的变量(|i|)的长,那么如果这段代码定义在顶层,即使循环结束了,i 也不会被回收的。 |
|