分享 Enumerator::Lazy 是干什么的?

hiveer · 2014年10月20日 · 最后由 hiveer 回复于 2014年10月21日 · 3395 次阅读

refs: http://railsware.com/blog/2012/03/13/ruby-2-0-enumerablelazy/ 这算是一次转载(翻译)吧!

曾经一度无限次的略过了 Enumerator::Lazy, 但蓦然回首间,发现它是多么的可爱!

我们从 ‘为什么’ 需要,来解释用它来干什么。

下面看个对比:

data.map(&:split).map(&:reverse)

data.map { |l| l.split.reverse }

这样的代码,我相信每个玩 ruby 的人都写过很多了。既然他们都是干 “同样的事情”,那么我们肯定要选择一个优雅的 (elegant),自然第一种写法更符合这种需求。注意,前一句话中 “同样的事情是打了引号的”,这里想强调的是,它们虽然结果是一样的,但是实现有很大不同。Ruby 是一门解释执行的语言,在解释器遇到第一种情况的时候,它将对 data 进行两次遍历,而第二种情况只会遍历一次。当这一次两次的差别遇到一个很大的数列的时候将会被无限放大,这必然不是我们想要见到的。

针对上面的假设,看下面的例子:

require 'prime'
Prime.select {|x| x % 4 == 3 }.take(10)

大家觉得上面的代码会花多长时间? 如果非要我告诉你一个答案的话,我希望是 “一万年”! 但是毛主席说过 “一万年太久 只争朝夕”,所以我们得改改才行:

a = []
Prime.each do |x|
  next if x % 4 != 3
  a << x
  break if a.size == 10
end

那么对比之后我们会发现相对优雅的代码的问题所在,那就是 Prime 是一个无限大的数列,我们将会无限的等待下去,除非你给她一个 ‘break' ! 等等,就这样了吗? 第二种方案有点 “丑觉不爱”,有木有? 当你有这个疑问的时候,是的!你应该需要Enumerator::Lazy的安慰了!

Prime.lazy.select {|x| x % 4 == 3 }.take(10).to_a

PS: 此文仅仅举了些许简单的例子,欢迎大家给出更多的应用场景,让小生们开开眼界!

自己顶了一个!

过来顶翻译贴

小生拜读了 😄

@ywjno @glz1992 谢谢两位的力挺!

学习了。 不过建议把第一段代码下面的"方法"改成"写法",method 也是方法,这样的话会混淆起来,让读者云里雾里。

@gyorou 很好的建议! @caojunvincent 感谢俊哥的赞哟!

#7 楼 @hiveer 不瞒你说,了解到这个前,我真的写过 break 的 low B 代码 :)

@caojunvincent 这个没关系啦,每个人从入门到精通都需要一个过程!谁没有一个 low B 的曾今呢? 哈哈

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