目前尚不清楚实质,但已经可以从形式上理解它的某些好处,有个很简单的连乘函数能够说明:函数
为了展现究竟发生了什么,我包装了下乘法函数,将其变为mul.spa
咱们将比较product和xproduct的区别.code
;包装乘法函数
(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))
结果:blog
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的方式就显得比较合理.class
product碰到0,即便可以停止递归过程,返回一个0,也仅此而已,它仍然须要笨拙地把这个0和前面已经存在的数进行连乘.lambda
而人脑认为这是愚蠢的,由于只要发现有一个0,那么不管其余数是怎样的,最终结果必然是0,因此直接返回0就能够了.call
call/cc正是在模拟人脑这一想法.di
下面展现xproduct函数,对于(1 2 3 0)和(1 2 3 4)两种状况,它的函数体分别是怎么展开的:co
> (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))))