二叉树下Haskell的延续传递方式



我正在学习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子句,但原理是一样的。

最新更新