最近折腾Clojure递归程序,在网上发现一个东西叫Y-Combinator,挺神奇的,号称能
把任何递归程序以非递归的形式写出来。
我看了一个blog,又引到另一个blog,讲的还行,但是不直接,我发现了一个比较直接
的讲法,所以写下来。
先定义一下问题。目标是定义一个Clojure函数Y,把任何给定的Clojure递归程序转化
为非递归形式。
比如一个factorial函数吧,我们命名它为w。它的递归程序是这样的,
(def w (fn [n] (if (= n 0) 1 (* n (w (- n 1))))))
这个定义可以写成这个形式:w=f(w)。其中f就是程序“形式”,也就是从括号fn开始
到括号结束的一堆字符,其中把w虚位,可以写成
f = (fn [n] (if (= n 0) 1 (* n (? (- n 1)))))
f(w)就是把问号换成w。f可以写成高阶函数的形式,
(def f (fn [w] (fn [n] (if (= n 0) 1 (* n (w (- n 1)))))))
f是递归函数w的“形式”,它本身不是递归函数。我们的目标是定义一个函数Y,或者
叫变换Y,使得Y(f)=w,这样我们就通过Y和f,两个非递归函数,复原了w这个递归函数
,达到了目标。
从w=f(w)这个形式,我们也可以说,w是f这个变换的不动点,fixed-point。所以我们
的目标也是找f的不动点,或者说是找一个变换,这个变换把f变为f的不动点,也就是Y
(f)=w。
这里最重要的一个trick是,定义一个函数u,such that,u(x):=f(x(x))。如果这样定
义了,那么代入u本身,
u(u)=f(u(u)),不动点有了,看得更清楚一点,令w=u(u),那么w=f(w)。所以w=u(u)就
是f的不动点。所以Y变换也有了,Y(f)=u(u)。
来我们检查一下这是不是一个well defined function。首先给定任意递归函数的“形
式”f,它本身不是递归的,然后定义u,such that u(x):=f(x(x)),这个函数是well
define的吧?呵呵,数学上它肯定不是,这只是Clojure里well defined。好吧,既然u
是well defined,那么u(u)也是well defined,因此Y这个把f变成w=u(u)的变换也是
well defined。
先写到这,怕丢了。函数Y的形式一会再写。写出来发现只剩一点点了,就合到这里。
f是给定函数,不需要写。先写u,
(def u (fn [x] (f (x x))))
再写w,
(def w (u u))
再写Y,
(def Y (fn [f] w))
合在一起,
(def Y (fn [f] ((fn [x] (f (x x))) (fn [x] (f (x x))))))
哦!u的定义有问题,(x x)这个形式在Clojure里编译的时候会出现stack overflow,
因为它要展开,这个地方我还没完全想明白。总之这里需要另一个trick,使编译器
defer 展开,(x x)其结果应该是一个函数,是函数就可以写成等价形式,
(x x) = (fn [y] ((x x) y))
用这个等价形式替换(x x)的出现(两次),就得到
(def Y (fn [f] ((fn [x] (f (fn [y] ((x x) y)))) (fn [x] (f (fn [y] ((x x) y)
))))))