Project1

标题: 请教for..in和each的区别 [打印本页]

作者: 烁灵    时间: 2011-1-19 13:16
标题: 请教for..in和each的区别
由于习惯用C/C++。。。吾辈喜欢用in来遍历,近来看到很多人都用each呢(尤其是67前辈。。
想问下这两种语句有什么优劣性呢?
作者: 洩矢諏訪子    时间: 2011-1-19 13:19
提示: 作者被禁止或删除 内容自动屏蔽
作者: fux2    时间: 2011-1-19 13:40
回复 烁灵 的帖子

回复 烁灵 的帖子

for 和each个人认为差不多
for是ruby自带语法
each是一个带块的方法,可以循环对象,一般是隶属于Proc类,也可以用Kernel的lambda函数
自己想像的伪代码:
  1. def each(str,&block)
  2.   block.call(str)
  3. end
复制代码

作者: 烁灵    时间: 2011-1-19 14:21
回复 fux2 的帖子

说起来,{}好奇里边的东西是怎么被赋值的,ruby可以传引用么?
作者: fux2    时间: 2011-1-19 14:27
回复 烁灵 的帖子

{}包括的部分是块代码
比如
  1. def fux2
  2.   yield if defined? yield
  3. end
复制代码
方法,
执行
  1. fux2{p "rpg.blue"}
复制代码
就会执行块里的代码
作者: enghao_lim    时间: 2011-1-19 15:20
each只是针对obj里所有的元素,相对for是循环。
each的处理因为省了一些阶段所以较快。
作者: 六祈    时间: 2011-1-21 00:29
本帖最后由 六祈 于 2011-1-21 00:35 编辑

回复 fux2 的帖子

fux君这个例子欠妥
愚者猜测你想表达,如果有代码块,则执行代码块,对吗?

如果是这样的话,应该是yield if block_given?

作者: 苏小脉    时间: 2011-1-21 01:24
本帖最后由 苏小脉 于 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 也不会被回收的。




欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1