cps-of-exps接受两个参数:一个【表达式的list】和一个【procedure builder】。
它在第一个参数中找到第一个不是【最简化单元】的表达式,如果找到了,就创建一个continuation K,这个K命名这个表达式的结果为一个新的变量,比如v1,然后递归调用被修改过(就是把那个【不是最简化单元】的表达式文字替换为v1)的【表达式的list】。
如果没有找到,则我们就把第二个参数【procedure builder】apply到【表达式的list】中去。
但遗憾的是,哪怕已经没有【非最简单元】了,【表达式的list】中已经都是最简单的了,他们也还是cps-in的语法,我们必须把它们转换成cps-out的语法。呃……话说回来,我们一开始的目的其实是把任何一个程序变成cps模式的。所以处理好一些不简单的表达式之后,简单的表达式也需要case每种情况稍稍处理一下。
所以我们先会把表达式都转换成out形式,然后再送给第二个参数【procedure builder】去apply去。
好,首先是一个inp-exp-simple,这个函数会拿到一个cps-in的表达式然后进行检查,是否可以被转化成cps-out。
如果是const表达式, 值是num,得到的是#t。
如果是var表达式,值是var,得到的是#t。
如果是diff-cont表达式,那么分别检查它的其中的两个值是不是。去and。
如果是zero?-exp表达式,则检查exp1的情况,如果是则是。
如果是proc-exp表达式,那么为#t
如果是sum-exp表达式,因为它是多参的,那么需要检查每一个exps。
不然就#f,表示不能解析噢。
cps-of-exps将sum表达式和procedure calls表达式都变成尾递归。sum表达式和precedure call中,都会有很多【非简化单元】,这个函数就是把那些非简化单元被简化的过程都做成尾递归的形式。
cps这个模式到底是怎么实现的,我觉得核心就是这个cps-of-exps函数了,因为可以明显的看到它复制了一份exp,并对它进行了更改。
(define cps-of-exps
(lambda (exps builder)
(let cps-of-rest ((exps exps))
(let ((pos (list-index
(lambda (exp)
(not (inp-exp-simple? exp)))
exps)))
(if (not pos)
(builder (map cps-of-simple-exp exps))
(let ((var (fresh-identifier 'var)))
(cps-of-exp
(list-ref exps pos)
(cps-proc-exp (list var)
(cps-of-rest
(list-set exps pos (var-exp var)))))))))))
最后面这段cps-of-exp中,做的事情非常重要:
取到那个非简单的exps表达式,然后对它进行cps-of-exp变换,变换后的结果当作新的形参,比如v1,然后走最下面这段,即建立一个cps-proc-exp结构,其中取一个变量,把变量替换到本来的表达式之中。这个cps-proc-exp结构,已经是一个cps-out的结构,于是无需再做处理了。
以上内容来自《eopl》,这本书真的非常棒!