Y combinator的简单理解

Y combinator本身是一个无状态函数,当用于另一个无状态的函数时,将返回该函数的递归版本。比如定义Y:(为了说明方便,用λi代替lambda关键字)

(defun y (f)
    ((
λ1 (x) (funcall x x))
     (λ2 (y)
         (
funcall f (λ3 (&rest args)
             (apply (funcall y y) args))))))


用计算Factorials的函数为例子:

(defun fac (f) 
    (λ4 (n)
        (if (zerop n)
            1
            (* n (funcall f (1- n))))))


那么(y #'fac)代表什么呢?


这里约定(A B)表示以B为参数调用A。以#'fac为参数进入y后,发生如下调用链:

(λλ2) -> (λλ2) -> (fac λ3) -> λ4

所以,(y #'fac)其实是一个以整数为参数的函数,进入REPL试试:

>(funcall (y #'fac) 5)

120


分析正确。那么是怎样得到120的呢? 从λ4继续:

(λλ2) -> (λλ2) -> (fac λ3) -> λ

                |                       |

                |  <-  (λ3 n-1)  <-No- 参数n为0? -Yes->  结束


所以这个Y版的fac虽然没有直接调用自己,但在Y的帮助下还是间接地调用了自己。


随便看看其它版本的factorial实现:


递归版:
(defun fac (n)

"一般递归"
    (if (< n 2)
        1
        (* n (fact (- n 1)))))

(defun fac (n)

"尾递归"
    (labels ((rec (n accum)
        (if (zerop)
            accum
            (rec (1- n) (* n accum)))))
         (rec n 1)))

(defun fac (n)

"仅供演示"
    (
funcall
        (
lambda (fn n)
            (funcall fn n fn))
        (
lambda (n this)
            (cond
                ((> n 0) (* n (funcall this (- n 1) this)))
                (t 1))) n)


迭代版:

( defun fac (n)
    ( loop for result = 1 then ( * result i)
           for i from 2 to n
           finally ( return result)))

函数版:
( defun fac (n) ( reduce #'* ( loop for i from 1 to n collect i)))

试试(fac 30000), 在ECL中也就几秒时间! 够强悍吧。(在Ruby, Python中可能早就溢出了)


可能很少有程序员用Y版来实现这样的函数, Y版的意义在于能让你更好地理解高阶函数,函数作为参数/返回值这些基本的FP概念。

你可能感兴趣的:(Lisp)