Scheme call/cc 研究

目前尚不清楚实质,但已经能够从形式上理解它的某些好处,有个很简单的连乘函数可以说明:

为了展示究竟发生了什么,我包装了下乘法函数,将其变为mul.

我们将比较product和xproduct的区别.

;包装乘法函数
(define (mul x y) 
  (display x) 
  (display " * ")
  (display y)
  (newline)
  (* x y))

;常规版
(define (product ls)
  (let f ((ls ls))
    (cond
      ((null? ls) 1)
      ((= (car ls) 0) 0)
      (else (mul (car ls) (f (cdr ls)))))))
;call/cc版
(define xproduct
  (lambda (ls)
    (call/cc
     (lambda (break)
       (let f ((ls ls))
         (cond
           ((null? ls) 1)
           ((= (car ls) 0) (break 0))
           (else (mul (car ls) (f (cdr ls))))))))))
;比较
(product '(1 2 3 4 5))
(xproduct '(1 2 3 4 5))
(product '(1 2 3 0 5))
(xproduct '(1 2 3 0 5))

结果:

5 * 1
4 * 5
3 * 20
2 * 60
1 * 120
120
5 * 1
4 * 5
3 * 20
2 * 60
1 * 120
120
3 * 0
2 * 0
1 * 0
0
0

我们可以看到,对不含0的连乘,两个函数的内部运行是一样的.

然而对于包含0的连乘,call/cc的方式就显得比较合理.

product碰到0,即使能够中止递归过程,返回一个0,也仅此而已,它仍然需要笨拙地把这个0和前面已经存在的数进行连乘.

而人脑认为这是愚蠢的,因为只要发现有一个0,那么无论其他数是怎样的,最终结果必然是0,所以直接返回0就可以了.

call/cc正是在模拟人脑这一想法.

下面展示xproduct函数,对于(1 2 3 0)和(1 2 3 4)两种情况,它的函数体分别是怎么展开的:

> (call/cc (lambda(break)(* 1(* 2(* 3 (break 0))))))
0
> (call/cc (lambda(break)(* 1(* 2(* 3 (* 4 1))))))
24
(let 
    ((@ (lambda (x) (display "@") x))
     (* (lambda (x) (display "*") x))
     (f (lambda (x) x)))
  ((@ (call/cc f))(* (call/cc f))))
原文地址:https://www.cnblogs.com/xiangnan/p/3436097.html