Y-Combinator

https://eecs.ceas.uc.edu/~franco/C511/html/Scheme/ycomb.html

一些情况下不需要给过程命名.

((lambda (x) (+ x 1)) 6)

递归函数能不命名么? 以阶乘函数fact为例, 下面逐步移掉对fact对名字的使用.

(define fact
  (lambda (x)
    (if (zero? x)
        1
        (* x (fact (- x 1))))))

第一步把名字当做参数传入.

(define op-maker
  (lambda (op)
    (lambda (x y)
      (op x y))))

用在阶乘这里, 即:

(define fact-maker
  (lambda (op)
    (lambda (x)
      (if (zero? x)
          1
          (* x (op (- x 1)))))))
> ((fact-maker fact-maker) 3)
*: contract violation
  expected: number?
  given: #<procedure:.../ycombinator.rkt:15:4>
  argument position: 2nd
  other arguments...:

不符合调用规则, 实际是不能工作的.

修改程序:

(define fact-maker
  (lambda (op)
    (lambda (x)
      (if (zero? x)
          1
          (* x ((op op) (- x 1)))))))
> ((fact-maker fact-maker) 3)
6

现在fact等价为(fact-maker fact-maker), 原地替换fact-maker则阶乘函数可以等价写为:

(define fact
  ((lambda (op)
     (lambda (x)
       (if (zero? x)
           1
           (* x ((op op) (- x 1))))))
   (lambda (op)
     (lambda (x)
       (if (zero? x)
           1
           (* x ((op op) (- x 1))))))))

这里实现了一个不需要名字的fact函数.

下面演示使用Y-combinator来泛化到所有递归函数上 .

先把计算阶乘的逻辑剥离出来.

(define F
  (lambda (n)
    (if (zero? n)
        1
        (* n ((op op) (- n 1))))))

我们知道:

(f arg) <=> ((lambda (x) (f x)) arg)

所以((op op) (- n 1))可以改写成:

((lambda arg) ((op op) arg) (- n 1))

替换F中的((op op) ...), 得到.

(define F
  (lambda (func-arg)
    (lambda (n)
      (if (zero? n)
          1
          (* n (func-arg (- n 1)))))
    (lambda (arg) ((op op) arg))))

现在用改写过的F替换掉上面fact中的对应逻辑,

(define fact
  ((lambda (op)
     ((lambda (func-arg)
       (lambda (n)
         (if (zero? n)
             1
             (* n (func-arg (- n 1))))))
      (lambda (arg) ((op op) arg))))
   (lambda (op)
     ((lambda (func-arg)
       (lambda (n)
         (if (zero? n)
             1
             (* n (func-arg (- n 1))))))
      (lambda (arg) ((op op) arg))))))

留意((lambda (func-arg) ...))这部分代码是阶乘函数的核心逻辑.将它提出来.

(define F*
  (lambda (func-arg)
       (lambda (n)
         (if (zero? n)
             1
             (* n (func-arg (- n 1)))))))

那么fact可以定义为:

(define fact
  ((lambda (op)
     (F* (lambda (arg) ((op op) arg))))
   (lambda (op)
     (F* (lambda (arg) ((op op) arg))))))

现在业务逻辑和无命名递归的结构剥离了出来.

fact的代码泛化. 得到Y组合子.

(define Y
  (lambda (X)
    ((lambda (op)
       (X (lambda (arg) ((op op) arg))))
     (lambda (op)
       (X (lambda (arg) ((op op) arg)))))))

fact函数用Y来表达出来, 即:

(define fact (Y F*))

我们使用Y组合子定义findmax函数.

(define M
  (lambda (func-arg)
    (lambda (xs)
      (if (null? xs)
          'none
          (if (null? (cdr xs))
              (car xs)
              (max (car xs) (func-arg (cdr xs))))))))
(define findmax (Y M))