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))
你也可以自己寻找更多类似的操作符。