Y combinator 推导过程

首先需要一个递归的函数

以阶乘为例
函数名为factorial,参数为n。如果n = 0返回1,否则返回factorial(n - 1)

定义一个辅助函数zero?,判断n是否等于0

(define (zero? n)
  (= n 0))

过程定义:

(define (factorial n)
  (if (zero? n)
      1
      (* n (factorial (- n 1)))))

换一种形式:

(define factorial
  (lambda (n)
    (if (zero? n)
        1
        (* n (factorial (- n 1))))))

消除函数名

Y combinator 目的就是消除递归中函数的名字。为了消除他,递归部分的函数名改为从参数中获取,也就是如下形式:

  (lambda (f)
    (lambda (n)
      (if (zero? n)
          1
          (* n (f (- n 1)))))))

那么我们要写的函数应用到自身返回目标函数,如下:

(define fact-factory
  (lambda (f)
    (lambda (n)
      (if (zero? n)
          1
          (* n ((f (- n 1)))))))

((fact-factory fact-factory) 5)

不能运行,因为参数fact-factory是的参数是函数类型,而n - 1是数字类型。将f改为(f f)

(define fact-factory
  (lambda (f)
    (lambda (n)
      (if (zero? n)
          1
          (* n ((f f) (- n 1)))))))

((fact-factory fact-factory) 5)

120

去掉函数名,就是如下形式:

(((lambda (f)
    (lambda (n)
      (if (zero? n)
          1
          (* n ((f f) (- n 1))))))
 (lambda (f)
    (lambda (n)
      (if (zero? n)
          1
          (* n ((f f) (- n 1))))))) 5)

推广

现在要做的就是把函数的定义和生成他的部分分离,函数定义作为一个参数传进去。

变换成如下形式:

(lambda (fun)
  (lambda (n)
    (if (zero? n)
        1
        (* n (fun (- n 1))))))

(f f)作为参数,该表达式的值就是最终的目标函数。

(lambda (n)
  ((fun-factory fun-factory) n))

fun-factory从参数获取,综合:

  (lambda (fun-factory)
    ((lambda (fun)
       (lambda (n)
         (if (zero? n)
             1
             (* n (fun (- n 1))))))
     (lambda (n)
       ((fun-factory fun-factory) n))))

把该函数应用到自身就可以得到目标函数了,并且定义部分完全独立。

那么我们在外面再套一层,定义部分作为参数。最终形式:

((lambda (fun-definition)
   ((lambda (fun-factory)
      (fun-definition (lambda (n) ((fun-factory fun-factory) n))))
    (lambda (fun-factory)
      (fun-definition (lambda (n) ((fun-factory fun-factory) n))))))
 (lambda (fun)
    (lambda (n)
      (if (zero? n)
          1
          (* n (fun (- n 1)))))))

代入参数正确运行。

其中的前半部分就是Y combinator,后半部分为函数定义。我们把他命名,留到后面使用。

(define Y
  (lambda (X)
    ((lambda (procedure)
       (X (lambda (arg) ((procedure procedure) arg))))
     (lambda (procedure)
       (X (lambda (arg) ((procedure procedure) arg)))))))

所以递归函数的只要写成如下形式,便可以完全消除函数名。

(lambda (fun)
  (lambda (args)
    (fun (abc))))

比如斐波那契数列就可以写成

  (lambda (fun)
    (lambda (n)
      (cond ((= n 0) 0)
            ((= n 1) 1)
            (else
             (+ (fun (- n 1)) (fun (- n 2)))))))

传入Y combinator

((Y
  (lambda (fun)
    (lambda (n)
      (cond ((= n 0) 0)
            ((= n 1) 1)
            (else
             (+ (fun (- n 1)) (fun (- n 2)))))))) 10)

55

附一个Java的Y combinator

    interface RecurFunc extends Function, T> {}

    public static  Function y(Function, Function> x) {
        RecurFunc> prod = f -> x.apply(arg -> f.apply(f).apply(arg));
        return prod.apply(prod);
    }

    public static void main(String[] args) {
        Function fact = y(f -> n -> n == 1 ? 1 : n * f.apply(n - 1));
        System.out.println(fact.apply(5));
    }

完。

http://www.ece.uc.edu/~franco/C511/html/Scheme/ycomb.html

你可能感兴趣的:(Y combinator 推导过程)