我正在学习CPS,我试图将下面的程序传递到这个样式
mirror Void = Void
mirror (Node x left right) = Node x (mirror right) (mirror left)
根据我的理解,我必须将延续传递给基本情况,并在递归情况下使用lambda构建结构。如果是和,则为
cpsadd n 0 k = k n
cpsadd n succ(m) k = cpsadd n m (v -> k succ(v))
--where k is the continuation, i.e, the stack.
list
的另一个例子mult [] k = k 1
mult (x:xs) k = mult xs (v -> k (x*v))
从这个意义上说,我有了第一个想法
mirror Void k = k Void
mirror (Node x l r) k = Node x (v -> k r v) (w -> k v r)
但我马上意识到我在构建树时没有传递延续k。所以我有了第二个想法
mirror Void k = k Void
mirror (Node x l r) k = mirror Node x (v -> k r v l)
现在我确实通过了延续,但是当我(手工)测试它时,我没有得到基本情况,所以它也没有工作。让我困惑的是,我必须调用递归函数两次,然后翻转它们来制作镜子。
任何想法?谢谢!
要达到CPS,你需要经常执行的一个基本转换是转向
f x y = g (h x) y -- non-CPS
到
f' x y k = h' x (r -> g' r y) -- CPS
也就是说,任何时候你需要在表达式的中间调用一个函数,你可以调用该函数的CPS版本,并给它一个lambda作为延续来结束表达式。因此,让我们从mirror
的定义开始,并努力实现CPS。
mirror Void = Void
mirror (Node x left right) = Node x (mirror right) (mirror left)
我写[e]
表示表达式e
需要转换成CPS形式,如果已经转换了,就写e
。首先,让我们添加k
参数,并将实现包装在括号中,以指示它们需要进行转换:
mirror Void k = [Void]
mirror (Node x left right) k = [Node x (mirror right) (mirror left)]
转换Void的情况很容易:你已经做过了。
mirror Void k = k Void
mirror (Node x left right) k = [Node x (mirror right) (mirror left)]
现在我们需要处理对mirror right
的第一个递归调用。我们立即调用它,然后给它一个lambda(还没有完全转换):
mirror Void k = k Void
mirror (Node x left right) k = mirror right r
where r right' = [Node x right' (mirror left)]
现在r
的主体有一个对mirror left
的调用,需要移除:
mirror Void k = k Void
mirror (Node x left right) k = mirror right r
where r right' = mirror left l
where l left' = [Node x right' left']
现在l
的主体没有递归调用了,并且正好有你想要传递给k
的值,所以最后的转换很容易:只调用k
。
mirror Void k = k Void
mirror (Node x left right) k = mirror right r
where r right' = mirror left l
where l left' = k $ Node x right' left'
如果你愿意,你可以用lambdas来代替where
子句,但原理是一样的。