我在
介绍 Continuation 的文章中提到了一个
create_iterator 的实现,它接受一个函数名,内部去调用这个函数而实现 iterator 。最初我是想让它接受一个 block 的,但是后来却改成了函数名,正是在做二叉树的遍历时出了问题:二叉树遍历是一个经典的递归程序,而 block 没有名字,无法做递归,只好作罢。
不过在
前一篇文章中我又介绍了 Y Combinator (其实我也是在一边学习的)正好可以用来做匿名递归,刚好可以把这两者结合在一起啦!
原来的
create_iterator 是这样的:
def create_iterator(travel_method)
caller = nil
travel = lambda do
self.send(travel_method) do |val|
callcc do |cont|
travel = cont
caller.call(val)
end
end
callcc { |cont| travel = cont }
caller.call(nil)
end
现在做一个
create_iterator_brk ,接受 block 而不是函数名作为参数,并把
create_iterator 做成它的浅包装:
def create_iterator_brk(brk)
caller = nil
travel = lambda do
brk.call(lambda do |val|
callcc do |cont|
travel = cont
caller.call(val)
end
end)
callcc { |cont| travel = cont }
caller.call(:end)
end
Iterator.new do
callcc do |cont|
caller = cont
travel.call
end
end
end
def create_iterator(travel_method)
create_iterator_brk(lambda { |brk|
self.send(travel_method, &brk)
})
end
这样就 OK 了,来测试一下 Y Combinator :
def y_comb(f)
lambda { |*args| f.call(y_comb(f)).call(*args) }
end
def test_y_comb
@tree.extend(ContinuationIterator)
it = @tree.create_iterator_brk(lambda { |b|
y_comb(lambda { |me|
lambda { |b, tree|
me.call(b, tree.left) unless tree.left.nil?
b.call(tree.value)
me.call(b, tree.right) unless tree.right.nil?
}
}).call(b, @tree)
})
seq = @inorder_seq.dup
until seq.first.nil? and it.value.nil?
assert_equal seq.first, it.value
seq.shift
it.inc
end
end
测试通过!通过 Y Combinator 来构造匿名递归函数,并通过 Currying 包装一下初始的
@tree 参数,就可以正常工作了!确实很有趣哦! :D
Comments (2)
粗粗看了一下,没看懂 =.=