Scheme call/cc 研究

目前尚不清楚實質,但已經可以從形式上理解它的某些好處,有個很簡單的連乘函數能夠說明:函數

爲了展現究竟發生了什麼,我包裝了下乘法函數,將其變爲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))))
相關文章
相關標籤/搜索