吃了大力丸的Ruby:unfold的实现

来源:互联网 发布:招聘软文 知乎 编辑:程序博客网 时间:2024/04/28 16:24

最近奇忙。从早到晚工作。别说写博客,连读博客都 没有时间。虽然一直想八卦一下JavaScript那浓眉大眼的也背叛革命了这件大事,却抽不出空。不过看到这么精彩的代码,还是忍不住转载。

起因是这样的:大家都熟悉fold函数,也就是Ruby里常用的inject()函数:给出起始值,把某个Collection里的值叠加上去。比如说,给出起始值0,累加数组[1, 2, 3, 4, 5]: 

[1, 2, 3, 4, 5].inject(0){|sum, n| sum = sum + n}

有了fold,便有相反的unfold:把单个的对象映射到Collection上。比如说 

10.unfold { |n| n-1 unless n == 1 }.inspect => [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]10.class.unfold(&:superclass).inspect => [Fixnum, Integer, Numeric, Object]

它的实现也出奇地漂亮:通过递归把unfold调用改写为数组操作。颇有term rewriting系统的风范: 

class Object
def unfold &block
block.call(self).unfold(&block).unshift(self)
end
end


class NilClass
def unfold &block
[]
end
end
用例子最容易说明这段实现的惊艳之处。我们手工执行3.unfold{|n| n - 1 unless n ==1}:
  1. 我们用block指代上式里的{|n| n - 1 unless n == 1}。根据unfold的定义,上式变为block.call(3).unfold(&block).unshift(3)
  2. 执行block.call(3)得到2,于是我们得到2.unfold(&block).unshift(3)
  3. 根据定义,我们得到block.call(2).unfold(&block).unshift(2).unshift(3)
  4. 执行block.call(2),我们得到1.unfold(&block).unshift(2).unshift(3)
  5. 再根据定义,我们得到block.call(1).unfold(&block).unshift(1).unshift(2).unshift(3)
  6. block.call(1)的结果是nil,所以我们得到nil.unfold(&block).unshift(1).unshift(2).unshift(3)
  7. nil早准备好了unfold的定义,于是我们得到[].unshift(1).unshift(2).unshift(3),也就是[3, 2, 1]了。

现在胃口被吊起来的老大们可以看这里的讨论了。

 
原创粉丝点击