跳至內容

Lisp 入門/第二章 表、CAR、CDR

維基教科書,自由的教學讀本

第二章 表、CAR、CDR

[編輯]

表和原子

[編輯]

Lisp的全名叫「表處理語言」,LISt Procesor 。可見表在Lisp中的重要性。

簡單說來,用小括號括起來的表達式式就叫表。

比如:

(+ 1 2)

就是一個表。

而表裏面的東西,就是原子。比如上面的這個表裏,+ 是原子,1 是原子,2 也是原子。

原子就是不包含空格的符號,可以是字符,也可以是數字。比如,下面的這個表裏,有兩個原子。

'(hello world)

這兩個原子分別是 hello 和 world。

其實,上面的那個式子,也是 LISP 語言中的 hello world 程序。很簡單,對吧。不過別忘記括號前面的單引號,否則會報錯。

表裏不僅可以包含原子,也可以包含另一個表。舉個例子:

(or (> 3 4) (> 4 2))

在上表中,or是原子,而 (> 3 4) 、(> 4 2) 都是表,不是原子。當然了,這兩個小表裏,包含的東西都是元素。

也就是說:表是可以嵌套的。

表的大小並沒有限制,最小的表就是空表:

()

程序和數據

[編輯]

編程這項工作,是在寫一個程序,而寫程序的目的是為了處理數據。程序與數據是編程中的兩大要素,而在 Lisp 中,這兩者都用 來表示。

如果你在解譯器中輸入一個表,那麼Lisp會對這個表求值。所以在解釋器中輸入

(+ 1 2)

會打印3。

說明這個表的值是3。

我們再看一個例子:

(1 2 3)

如果你在解釋器中輸入上面的表達式,會得到一個錯誤:Error: 1 is invalid as a function. 意思是1不是一個可用函數。

在所有的表中,第一個原子總是函數,代表操作、指令、命令。而之後原子(或表)是參數,意即對操作的說明。所以 (+ 1 2) 表示 1 + 2,而 (- 4 3) 表示 4 -3。

例外是這個

()

這是一個空表,會返回

NIL

NIL是一個原子,表示邏輯假,但它同時也是空表。它是LISP語言中唯一一個既是表又是原子的東西。

程序是表,那數據該如何表示? 數據也是用表來表示。

Lisp會對所有的表求值,但如果我們想使用表本身(作為數據),這反而會成為一件麻煩事。只要用一個簡單的操作符就可以防止求值,那就是 ' 操作符(單引號)。

'(+ 1 2)

這次解譯器不對這個表其求值了,結果不再是3這個原子,而是一個表,原封未動的表。

(+ 1 2)

還記得我們的 hello world 程序嗎?

'(Hello world!)

會返回

(HELLO WORLD!)

各種程序語言的入門書籍都喜歡一個hello, world程序。在此鄭重告訴大家, Lisp 也有自己的 Hello,world!

上面我們輸入的是小寫的字母,輸出的卻是大寫的字母。這是因為在 Lisp 中大小寫無所謂。

不過 ' 這個東西其實只是一個語法糖(為了讓程式設計師少打幾個字符而創造的語法)。它其實是一種簡寫,全稱是quote操作,意思是引用。上面的hello world 程序如果寫全了,就是:

(quote (Hello world!))

CAR 操作符

[編輯]

CAR操作符的作用是取出表的第一個元素。

(car '(1 2 3 4 5))

上表會返回 1。

CAR操作符的作用是取出表的第一個元素,注意,我說的是元素不是原子,所以car的返回值也可能是個表。

讓我們來試試

(car '((1 2) 3))

返回的值就是一個表。CAR 的作用就是取出第一個元素,至於第一個元素是表還是原子,它並不關心。

不過,CAR 這個名稱真的是很古老了,我們可以用 first 操作符來替代它。實際上,first 是 CAR 的別名。

注意到,我們上面的代碼在參數表是由一個單引號(引用操作)引導的。這是非常重要的。如果我們去掉單引號。

(car (1 2 3 4 5))

系統將會報錯: error: 1不是可用的函數

如果大家還記得我們之前的報錯,就會知道,這是因為系統試圖執行內表中的代碼。是的,在 LISP 中,表就是程序。 如果沒有特殊說明系統會對一切看得到的表求值。所以,我們要加上單引號,表示想要以數據的形式使用這個表。

還有一個事情是值得注意的,CAR 操作只對表管用,不可以把它用在原子上。比如:

(car 1)

系統會返回錯誤: error: 1不是「表」類型

CDR 操作符

[編輯]

CDR 操作符和 CAR 的作用是相反的,它的作用是去除表的第一個元素。

(cdr '(1 2 3 4 5))

上面表達式的返回值是

(2 3 4 5)

CDR 總是返回一個表。它的意義,就是取出表中除了第一個元素之外的所有元素,然後返回這些元素組成的表。

CDR 的別名是 rest,這也是非常形象的名字,意思是其餘,即除了第一個以外的其餘。

我們可以用CDR操作符取出函數的參數,比如

(cdr '(+ 1 2 3))

這行代碼可以取出(+ 1 2 3)的參數(1 2 3)。

如果表中只有一個元素,那麼對這個表進行 CDR 操作,將會發生什麼事情呢?讓我們來試驗一下:

(cdr '(1))

將會返回 NIL。不要忘記,NIL 也代表一個空表。所以,上面的表達式返回的其實是一個空表。

通過了解 CAR 和 CDR操作符,你會有種感覺,第一個元素竟然和其他所有元素平等。是的,這確實是 LISP 的世界觀,至於為什麼,你讀下去就會知道。

CXR 組合操作符

[編輯]

問你一個問題,如何取出第二個元素?

先不要急着往下看,好好想想。

我們至今只接觸了兩個運算符,一個是CAR,可以取出第一個,另一個是CDR,可以取出其餘的。那麼如何用這兩個運算符取出第二個元素呢?

答案在下面:

(car (cdr '(1 2 3)))

第二個元素就是去掉第一個元素之後的第一個元素。我們先用CDR取出除第一個元素外的其他元素(本質上就是去掉了第一個元素),然後就可以在這個新表中取出第一個元素了,這樣,我們得到的就是原表的第二個元素。

哇,聰明如你,是不是悟到了一種方法,可以取任何第幾個元素。

那思考一下如何取第三個元素吧,寫出來,在解譯器上試驗一下吧

恭喜你,你的第一個原創Lisp代碼實現了。如果還沒實現,那麼恭喜你,下面是代碼:

(car (cdr (cdr '(1 2 3))))

實際上,LISP 中由專門的操作符來表示這些操作,比如 CADR

試試下面的代碼:

(cadr '(1 2 3))

你也可以自己尋找更多類似的操作符。