資料的過程性表示

方應杭在飢人谷發表於2018-12-01

夜裡睡不著看起了 SICP,這裡是第二章《構造資料抽象》的筆記。

知識點一:序對

(cons x y) 表示一個序對,記為 z。
(car z) 可以獲取 z 的第一個元素 x。
(cdr z) 可以獲取 z 的第二個元素 y。
複製程式碼

cons(讀作控死)、car(讀作卡) 和 cdr(讀作酷得兒)

翻譯成 JS 風格的虛擬碼就是

z = cons x y
car z // 值為 x
cdr z // 值為 y
複製程式碼

知識點二:資料的過程性表示

有沒有可能 cons、car 和 cdr 其實是三個過程(也就是函式)呢?

只要 cons、car 和 cdr 三個函式滿足上面的要求即可,下面是這三個函式的寫法:

(define (cons x y)
  (define (dispatch m)
    (cond ((= m 0) x)
          ((= m 1) y)
          (else (error "argument not 0 or 1 -- cons" m))))
  despatch)

(define (car z) (z 0))
(define (cdr z) (z 1))
複製程式碼

翻譯成 JS 大概是這樣的:

function cons(x, y){
  // 直接返回一個函式
  return function dispatch(m){
    if(m===0){return x}
    if(m===1){return y}
    throw new Error('argument not 0 or 1 -- cons' + m)
  }
}

function car(z){
  return z(0)
}

function cdr(z){
  return z(1)
}
複製程式碼

這裡令人驚奇的地方就在於 cons 直接返回了一個函式,並沒有返回一個陣列之類的玩意。

不過這只是一種模擬,語言內部並不是這樣實現的。這種程式設計風格叫做「訊息傳遞」。

知識點三

還可以這樣定義 cons、car、cdr

(define (cons x y)
  (lambda (m) (m x y)))
(define (car z)
  (z (lambda (a b) a)))
(define (cdr z)
  (z (lambda (a b) b))
複製程式碼

翻譯成 JS 是這樣

cons = (x, y)=> {
  return (m) => m(x, y)
}
car = z => {
  return z((a, b) => a)
}
cdr = z => {
  return z((a, b) => b)
}
複製程式碼

寫成這樣還挺燒腦的。

知識點四

上面的知識點說明其實可以拋棄複合資料結構,全用函式就行了。

但是如果我告訴你,連數字都可以不需要呢?(假設只考慮非負整數,而且這種語言允許我們對函式進行任何操作)

0 用一個函式來表示

zero = f => x => x // 這裡的 f 看起來沒有意義,要到下面才能理解
複製程式碼

加1操作用另一個函式來表示

add1 = n => f => x => f(n(f)(x))
複製程式碼

1、2 的表示方法見習題答案

zero= f => x => x
one = f => x => f(x)
two = f => x => f(f(x))
複製程式碼

這樣定義了之後,可以證明

add1(zero) 得到的函式跟 one 是一模一樣的(但不是同一個函式);

add1(one) 得到的函式跟 two 是一模一樣的(但不是同一個函式);

雖然我不知道這樣做有什麼意義,不過還是很震撼的。

知識點五:cons 的閉包性質(與 JS 的閉包不是同一個術語)

一般來說,某種組合資料物件的操作滿足閉包性質是指:通過它組合起資料物件得到的結果本身還可以通過同樣的操作再進行組合。

換句話說:組合式的成員本身還可以是組合式的。

比如

(const (cons 1 2) 
       (cons 3 4))
複製程式碼

cons 組合 1、2 得到的資料,還可以作為 cons 的元素,所以說 cons 滿足閉包性質。

知識點六:連結串列

(cons 1 
  (cons 2 (
    cons 3 (
      cons 4 nil))))
複製程式碼

通過 cons 的這種性質,很容易用 cons 表示一個連結串列。

資料的過程性表示

這種語法可以簡化為

(list 1 2 3 4)
複製程式碼

注意 (list 1 2 3 4) 是一個函式呼叫,其返回值為(1 2 3 4)。假設 one-to-four = (list 1 2 3 4)

(define one-to-four (list 1 2 3 4))
複製程式碼

car one-to-four 就是獲取表頭,cdr one-to-four 就是獲取表頭之外的節點。

我們可以用 cons 來在連結串列中插入節點

(cons 10 one-to-four)
複製程式碼

結果為(10 1 2 3 4)。

還挺有意思的。

未完待續……

第三章的筆記:juejin.im/post/5c04de…

相關文章