目前尚不清楚實質,但已經可以從形式上理解它的某些好處,有個很簡單的連乘函數能夠說明:函數
爲了展現究竟發生了什麼,我包裝了下乘法函數,將其變爲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))))