sicp每日一題[2.6]

再思即可發表於2024-09-12

Exercise2.6

In case representing pairs as procedures wasn't mind-boggling enough, consider that, in a language that can manipulate procedures, we can get by without numbers (at least insofar as nonnegative integers are concerned) by implementing 0 and the operation of adding 1 as

(define zero (lambda (f) (lambda (x) x)))
(define (add-1 n)
  (lambda (f) (lambda (x) (f ((n f) x)))))

This representation is known as $Church numerals$, after its inventor, Alonzo Church, the logician who invented the λ calculus.
Define one and two directly (not in terms of zero and add-1). (Hint: Use substitution to evaluate (add-1 zero)). Give a direct definition of the addition procedure + (not in terms of repeated application of add-1).


這個題太難理解了,題目裡的兩個函式,zero 我搞明白了,也勉強能接受它跟 0 的作用相同,但是我始終理解不了為啥 add-1 的作用是加一,明明是 (f x) 啊。

; 任何數加 0 都等於它本身。所以,表示 0 的函式,無論你給它傳入什麼函式 f 和引數 x,它都直接返回 x,相當於什麼都不做。
; (lambda (f) ...) 定義了一個匿名函式,它接受一個函式 f 作為引數,並返回 (lambda (x) x)。
; (lambda (x) x) 定義了另一個匿名函式,它接受一個引數 x,並直接返回 x。
; 組合起來:zero 函式本質上就是一個恆等函式,無論你給它傳入什麼函式 f,它都會返回一個新的函式,這個新函式會直接返回它的輸入 x。
; 這符合我們對 0 的理解————任何數加 0 等於它本身。
(define zero (lambda (f) (lambda (x) x)))

; 1. 把 (lambda (x) (square x)) 傳給 zero 作為引數,返回新的函式 (lambda (x) x)
; 2. 再把 10 傳給 (lambda (x) x),得到原數 10
(display ((zero (lambda (x) (square x))) 10))    ; 輸出是 10
(newline)

; n 必須是 Church Numeral,比如說上面定義的 zero
; 返回值是一個新的 Church Numeral,表示的值為 n + 1
(define (add-1 n)
  (lambda (f) (lambda (x) (f ((n f) x)))))


(display (((add-1 zero) square) 10))    ; 輸出是 100
(newline)

; 1. 先用 f1 表示匿名函式 (lambda (x) (+ x 1)),則原式變為 ((zero f1) 0)
; 2. 把 zero 函式用它的定義替代,得到 (((lambda (f) (lambda (x) x)) f1) 0)
; 3. 把 f1 帶入到 zero 的返回值函式,得到 ((lambda (x) x) 0)
; 4. (lambda (x) x) 函式會返回傳入的引數,也就是0
((zero (lambda (x) (+ x 1))) 0)    ; 輸出是 0

; 1. 把 zero 作為引數傳入,得到 (lambda (f) (lambda (x) (f ((zero f) x))))
; 2. 先處理 (zero f),根據上面我們對 zero 函式的分析,無論傳入什麼函式作為引數,它都返回 (lambda (x) x)
; 3. 則 ((zero f) x) 就是 x,(lambda (f) (lambda (x) (f x))),也就是無論傳入什麼 f 和 x,都會返回 (f x)
(define one (add-1 zero))

; 根據上面的分析,下面的程式相當於執行 ((lambda (x) (+ x 1)) 0), 也就是 0 + 1
((one (lambda (x) (+ x 1))) 0)    ; 輸出是 1

; 1. 把 one 作為引數傳入,得到 (lambda (f) (lambda (x) (f ((one f) x))))
; 2. 根據上面的分析,((one f) x) 會返回 (f x)
; 3. 所以下面的函式無論傳入什麼 f 和 x,都會返回 (f (f x))
(define two (add-1 one))

; 根據上面的分析,下面的程式相當於執行 ((lambda (x) (+ x 1)) ((lambda (x) (+ x 1)) 0)), 也就是 ((0 + 1) + 1)
((two (lambda (x) (+ x 1))) 0)    ; 輸出是 2

; 以此類推,可以用如下方式直接定義 one, two, three
(define new-one (lambda (f) (lambda (x) (f x)))) 
(define new-two (lambda (f) (lambda (x) (f (f x)))))
(define three (lambda (f) (lambda (x) (f (f (f x)))))) 

; 雖然不理解為啥 add-1 可以表示加一,但是按照上面的規律,加法可以按下面的函式來定義
(define (add a b) 
  (lambda (f) 
    (lambda (x) ((a f) ((b f) x)))))

; 下面的程式相當於 ((two square) (square 2))
; 進一步替代得到 (square (square (square 2)))
(((add two one) square) 2)      ; 輸出是 256

; 執行結果
10
100
0
1
2
256

相關文章