分享 Ruby1.9 数组,哈希,枚举,集合的迭代器方法归纳与比较

zw963 · 2012年02月28日 · 最后由 zhangyanan 回复于 2013年11月22日 · 7259 次阅读

注意: 以下表格来源于 zw963 的 Ruby 学习笔记. 只不过为了方便好看, 通过 org 转化为 网页并打印成图片上传到 ruby-china 分享版块. 欢迎随意转载.

# 表示 可以用,但是意义不大.

乱了 乱了

@hooopo, 复制到一个文本文件中再看啊, 这都是 orgmode 的表格, 保证贼整齐.

LZ 不建议用 reduce,可是 reduce 的速度要比 inject 快很多。。。

这样不错

更新成为一个大图片啦!! 欢迎大家多提建议. 也给我这个新手改正的机会.

@messiahxu, 我没有说不建议用 reduce 啊. 在哈希那一列的那个 #, 是指哈希也可以用 reduce 方法, 但是意义不大.

#6 楼 @zw963 好吧,另外你后面说的 inject 可以省略代码块方式直接用参数,实际上,reduce 也可以,因为这两个方法根本就是一样的,唯一不同的就是 reduce 的速度比 inject 快很多,原因我到现在都没搞清楚

这 2 个不是同一个方法么,刚刚试了下,没发现 reduce 快很多。

#8 楼 @kenshin54 你用 1..1000000 试试,1.9.2 里面 reduce 速度几乎是 inject 的一倍

@messiahxu 试了啊 inject 0.098785 reduce 0.097086

#10 楼 @kenshin54 那我郁闷了 难道我机器问题?1.9.3 里的确没什么差别

@messiahxu,你一定搞错了, reduce 和 inject 没有效率方面的区别. 正如你说, 他们是一样的.我只所以分开写, 只是希望自己养成不同的风格 (inject 代码块, reduce 使用符号)

但是以下两段代码区别很大:

reduce(:+)              # => 效率高
reduce(&:+)             # => 效率极低

至于原因, 我大概可以猜到, Ruby 一定使用甚么方式特别优化了 inject 算法, 比单纯的传递符号方式要好.

#12 楼 @zw963 我这里 1.9.3 做出来 inject 和 reduce 效率就差不多了,不过用参数不用代码块的确要快一点,另外我这里 reduce(&:+) 效率没差阿

@messiahxu, 我说 inject 和 reduce 效率差不多, 测试环境式 1.92 不是 1.93, 通过循环分别测试了 10 次, 上下幅度范围很小. 通过&:proc 方式, 1.92 下效率真的很差, 也许 1.93 有所改善.不过我没测试过.

@zw963 @messiahxu 在 Ruby 的 C 实现里,inject 就是 reduce,reduce 就是 inject,没有区别。C 代码写的明明白白:

rb_define_method(rb_mEnumerable, "inject", enum_inject, -1);
rb_define_method(rb_mEnumerable, "reduce", enum_inject, -1);

都是 enum_inject 这个函数来实作的。

那图是一张网页,能给出网址吗?

@zw963 @messiahxu

reduce(:+)       
reduce(&:+)   

这两种写法是有本质区别的:

  1. 第一种写法 实际上是 enum.reduce(sym) 这个形式,sym 只是普通参数。
  2. 而第二种形式,可以去掉括号,就是 reduce &:+ 。这个&很关键,它作用于支持 to_proc 的对象前,以这个例子说,在调用中,Ruby 会把这个 proc 作为 reduce 的代码块 来调用。

所以,第一种是以普通形式调用,第二种则是以代码块形式调用。所以第二种形式略慢于第一种。

注意:任何方法调用都可以用一个&参数作为最后一个参数 举例:

def x
  yield 'abcd' if block_given?
end

puts x &:upcase 

@skandhas, 你讲的太清楚了, 赞一个.

reduce(&:符号) 的形式, 等价于常规的代码块形式. 这我知道, 不过你还是没说清楚 reduce(:符号) 这种情况内部是如何实现的. 我看不懂 C 源码, 不过之前我猜测, 可能仍旧跟代码块类似形式有关, 只不过针对 reduce 功能做了某种优化而已.看样子我的想法是不对的.

#18 楼 @zw963 粗略的来说reduce(:符号)这种形式,在迭代元素时,可以说是直接在迭代元素上调用:符号对应的方法. 而 reduce 代码块形式,则是在迭代时,把元素 yield 给代码块,这样性能开销略微大一些。

通常,这两者性能差别不大。如果元素数量很多多时,则能显现出来。 一般来说,我觉得如果性能并不敏感(或是在可接受范围内)的话,具体采用哪种写法,以个人习惯或是以代码是否清晰,容易阅读为选择标准吧。 :>

@skandhas, 突然一下明白啦. 其实区别就是有没有 yield 这个过程, 对吧. 哈哈. 之前的想法掉到盒子里了....

现在想明白了. 其实就是: (比喻下)

"this is a string".upcase       # => 这是前者. 在字符串对象上直接调用
"this is a string".each_line {|line| line.upcase} # => 这是后者

#20 楼 @zw963

names.map &:upcase

是调用Symbol#to_proc:upcase进行类型转换为 block object, 以下是一种实现

class Symbol
  def to_proc
    proc { |obj, *args| obj.send(self, *args) }
  end
end

这里调 send 方法当然很慢,实际的 c 源码实现效率应该高不少

总结滴灰常好哟~~~~~!

@zw963 想说怎么转载啊?

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册