设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
查看: 3228|回复: 7
打印 上一主题 下一主题

[讨论] 请教for..in和each的区别

[复制链接]

Lv1.梦旅人

梦石
0
星屑
110
在线时间
953 小时
注册时间
2007-4-25
帖子
805
1
发表于 2011-1-21 01:24:42 | 显示全部楼层
本帖最后由 苏小脉 于 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. [1].each do
  2.     i = 2
  3. end

  4. p i # error
复制代码
除非在这个 each 的块的词法范围之外也出现了一个 i = 的赋值语句,这个判定是在词法分析时,一旦被 Ruby 词法分析器看见就知道是局部变量,并不一定要执行:

  1. if false
  2.     i = 1
  3. end

  4. [1].each do
  5.     i = 2
  6. end

  7. p i # OK
复制代码
效率上——对于,Ruby 1.8 得看具体解释器是如何处理的,理论上由于是直接在抽象文法树上求值,两者在文法分析时都会被剖析为一样的文法树节点,所以在执行时都没有区别,区别应该在于局部变量的创建,这个可以通过 1.9 YARV 编译 Ruby 代码后的指令来分析:

  1. [1].each do
  2. end
复制代码
这一段反汇编后是:

  1. |------------------------------------------------------------------------
  2. 0000 trace            1                                               (   1)
  3. 0002 duparray         [1]
  4. 0004 send             :each, 0, block in <compiled>, 0, <ic:0>
  5. 0010 leave
  6. == disasm: <RubyVM::InstructionSequence:block in <compiled>@<compiled>>=
  7. == catch table
  8. | catch type: redo   st: 0000 ed: 0001 sp: 0000 cont: 0000
  9. | catch type: next   st: 0000 ed: 0001 sp: 0000 cont: 0001
  10. |------------------------------------------------------------------------
  11. local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1] s3)
  12. [ 2] i<Arg>
  13. 0000 putnil                                                           (   2)
  14. 0001 leave
复制代码
而如果是:

  1.     for i in [1]
  2.     end
复制代码
反汇编后:

  1. local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)
  2. [ 2] i
  3. 0000 trace            1                                               (   1)
  4. 0002 duparray         [1]
  5. 0004 send             :each, 0, block in <compiled>, 0, <ic:0>
  6. 0010 leave
  7. == disasm: <RubyVM::InstructionSequence:block in <compiled>@<compiled>>=
  8. == catch table
  9. | catch type: redo   st: 0005 ed: 0006 sp: 0000 cont: 0005
  10. | catch type: next   st: 0005 ed: 0006 sp: 0000 cont: 0006
  11. |------------------------------------------------------------------------
  12. local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1] s3)
  13. [ 2] ?<Arg>
  14. 0000 getdynamic       *, 0                                            (   2)
  15. 0003 setlocal         i                                               (   1)
  16. 0005 putnil                                                           (   2)
  17. 0006 leave
复制代码
可见 Ruby 在处理 for 循环的循环局部变量 i 时,是需要额外的指令周期的。同时,由于它的生命周期比块内部处理的变量(|i|)的长,那么如果这段代码定义在顶层,即使循环结束了,i 也不会被回收的。

点评

不能说是 each 有作用域,只能说是 Ruby 的块有。  发表于 2011-1-23 00:39
就是这个,我之前还以为each没有作用域,知道几个月前也做了这个测试才知道。==  发表于 2011-1-23 00:09
啊咧……汇编语音不懂……  发表于 2011-1-21 07:10
[email protected]:~> repeat 1 fortune
Matz is nice, so we are nice.
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-5-6 10:06

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表