注意:以下表格来源于 zw963 的 Ruby 学习笔记。只不过为了方便好看,通过 org 转化为 网页并打印成图片上传到 ruby-china 分享版块。欢迎随意转载。
# 表示 可以用,但是意义不大.
@messiahxu,你一定搞错了,reduce 和 inject 没有效率方面的区别。正如你说,他们是一样的。我只所以分开写,只是希望自己养成不同的风格 (inject 代码块,reduce 使用符号)
但是以下两段代码区别很大:
reduce(:+) # => 效率高
reduce(&:+) # => 效率极低
至于原因,我大概可以猜到,Ruby 一定使用甚么方式特别优化了 inject 算法,比单纯的传递符号方式要好。
@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 这个函数来实作的。
reduce(:+)
reduce(&:+)
这两种写法是有本质区别的:
reduce &:+
。这个&很关键,它作用于支持 to_proc 的对象前,以这个例子说,在调用中,Ruby 会把这个 proc 作为 reduce 的代码块 来调用。所以,第一种是以普通形式调用,第二种则是以代码块形式调用。所以第二种形式略慢于第一种。
注意:任何方法调用都可以用一个&参数作为最后一个参数 举例:
def x
yield 'abcd' if block_given?
end
puts x &:upcase
@skandhas, 你讲的太清楚了,赞一个。
reduce(&:符号) 的形式,等价于常规的代码块形式。这我知道, 不过你还是没说清楚 reduce(:符号) 这种情况内部是如何实现的。 我看不懂 C 源码,不过之前我猜测,可能仍旧跟代码块类似形式有关, 只不过针对 reduce 功能做了某种优化而已。看样子我的想法是不对的。
@skandhas, 突然一下明白啦。其实区别就是有没有 yield 这个过程,对吧。哈哈. 之前的想法掉到盒子里了....
现在想明白了. 其实就是:(比喻下)
"this is a string".upcase # => 这是前者. 在字符串对象上直接调用
"this is a string".each_line {|line| line.upcase} # => 这是后者
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 源码实现效率应该高不少