Lisp 整体思想

形式(Form)

人可以通过实践来学习一件事,这对于 Lisp 来说特别有效,因为 Lisp 是一门交互式的语言。任何 Lisp 系统都含有一个交互式的前端,叫做顶层(toplevel)。你在顶层输入 Lisp 表达式,而系统会显示它们的值。
许多 Common Lisp 的实现用 > 作为顶层提示符。

$ clisp
> 1
1
>

打印的值与输入的值相同,数字 1 称之为对自身求值。

> (+ 2 3)
5

在表达式 (+ 2 3) 里, + 称为操作符,而数字 23 称为实参。
在 Lisp 里,把 + 操作符写在前面,接着写实参,再把整个表达式用一对括号包起来。
这称为 「前序表达式」。

日常生活的表示法,要写两次 + 号 2 + 3 + 4,在 Lisp 里,只需要增加一个实参:(+ 2 3 4)

日常生活中用 + 时,它必须有两个实参,一个在左,一个在右。前序表示法的灵活性代表着,在 Lisp 里, + 可以接受任意数量的实参,包含了没有实参:

> (+)
0
> (+ 2)
2
> (+ 2 3)
5
> (+ 2 3 4)
9
> (+ 2 3 4 5)
14

由于操作符可接受不定数量的实参,我们需要用括号来标明表达式的开始与结束。
表达式可以嵌套。即表达式里的实参,可以是另一个复杂的表达式:

> (/ (- 7 1) (- 4 2))
3

所有的 Lisp 表达式,要么是 1 这样的数原子,要么是包在括号里,由零个或多个表达式所构成的列表。以下是合法的 Lisp 表达式:

2 (+ 2 3) (+ 2 3 4) (/ (- 7 1) (- 4 2))

所有的 Lisp 程序都采用这种形式。而像是 C 这种语言,有着更复杂的语法:算术表达式采用中序表示法;函数调用采用某种前序表示法,实参用逗号隔开;表达式用分号隔开;而一段程序用大括号隔开。

求值(Evaluation)

在 Lisp 里, + 是函数,然而如 (+ 2 3) 的表达式,是函数调用。
当 Lisp 对函数调用求值时,它做下列两个步骤:

  1. 首先从左至右对实参求值。在这个例子当中,实参对自身求值,所以实参的值分别是 23
  2. 实参的值传入以操作符命名的函数。在这个例子当中,将 23 传给 + 函数,返回 5

如果实参本身是函数调用的话,上述规则同样适用。以下是当 (/ (- 7 1) (- 4 2)) 表达式被求值时的情形:

  1. Lisp 对 (- 7 1) 求值: 7 求值为 7 , 1 求值为 1 ,它们被传给函数 - ,返回 6 。
  2. Lisp 对 (- 4 2) 求值: 4 求值为 4 , 2 求值为 2 ,它们被传给函数 - ,返回 2 。
  3. 数值 6 与 2 被传入函数 / ,返回 3 。

但不是所有的 Common Lisp 操作符都是函数,不过大部分是。函数调用都是这么求值。由左至右对实参求值,将它们的数值传入函数,来返回整个表达式的值。这称为 Common Lisp 的求值规则

如果输入 Lisp 不能理解的东西,它会打印一个错误讯息,接着带你到一种叫做中断循环(break loop)的顶层。输入 :abort 跳出:

> (/ 1 0)
Error: Division by zero
      Options: :abort, :backtrace
>> :abort
>

一个不遵守 Common Lisp 求值规则的操作符是 quotequote 是一个特殊的操作符,意味着它自己有一套特别的求值规则。这个规则就是:什么也不做。quote 操作符接受一个实参,并完封不动地返回它。

> (quote (+ 3 5))
(+ 3 5)

使用缩写 ' 比使用整个 quote 表达式更常见。
Lisp 提供 quote 作为一种保护表达式不被求值的方式。

数据(Data)

Lisp 提供了所有在其他语言找的到的,以及其他语言所找不到的数据类型。一个我们已经使用过的类型是整数(integer),整数用一系列的数字来表示,比如: 256 。另一个 Common Lisp 与多数语言有关,并很常见的数据类型是字符串(string),字符串用一系列被双引号包住的字符串表示,比如: "ora et labora"。整数与字符串一样,都是对自身求值的。

有两个通常在别的语言所找不到的 Lisp 数据类型是符号(symbol)与列表(lists),符号是英语的单词 (words)。无论你怎么输入,通常会被转换为大写:

> 'Artichoke
ARTICHOKE

列表是由被括号包住的零个或多个元素来表示。元素可以是任何类型,包含列表本身。使用列表必须要引用,不然 Lisp 会以为这是个函数调用:

> '(my 3 "Sons")
(MY 3 "Sons")
> '(the list (a b c) has 3 elements)
(THE LIST (A B C) HAS 3 ELEMENTS)

注意引号保护了整个表达式(包含内部的子表达式)被求值。
你可以调用 list 来创建列表。由于 list 是函数,所以它的实参会被求值。这里我们看一个在函数 list 调用里面,调用 + 函数的例子:

> (list 'my (+ 2 1) "Sons")
(MY 3 "Sons")

为什么我们需要 quote,如果一个列表被引用了,则求值规则对列表自身来求值;如果没有被引用,则列表被视为是代码,依求值规则对列表求值后,返回它的值。

> (list '(+ 2 1) (+ 2 1))
((+ 2 1) 3)

在 Common Lisp 里有两种方法来表示空列表:

> ()
NIL
> nil
NIL

列表操作(List Operations)

用函数 cons 来构造列表。如果传入的第二个实参是列表,则返回由两个实参所构成的新列表,新列表为第一个实参加上第二个实参:

> (cons 'a '(b c d))
(A B C D)

可以通过把新元素建立在空表之上,来构造一个新列表。
list,不过就是一个把几个元素加到 nil 上的快捷方式:

> (cons 'a (cons 'b nil))
(A B)
> (list 'a 'b)
(A B)

取出列表元素的基本函数是 carcdr 。对列表取 car 返回第一个元素,而对列表取 cdr 返回第一个元素之后的所有元素:

> (car '(a b c))
A
> (cdr '(a b c))
(B C)
> (car (cdr (cdr '(a b c d))))
C
> (third '(a b c d))
C

真假(Truth)

在 Common Lisp 里,符号 t 是表示逻辑  的缺省值。与 nil 相同, t 也是对自身求值的。如果实参是一个列表,则函数 listp 返回  :

> (listp '(a b c))
T

函数的返回值将会被解释成逻辑  或逻辑  时,则称此函数为谓词(predicate)。在 Common Lisp 里,谓词的名字通常以 p 结尾。
逻辑  在 Common Lisp 里,用 nil ,即空表来表示。如果我们传给 listp 的实参不是列表,则返回 nil 。

> (listp 27)
NIL

由于 nil 在 Common Lisp 里扮演两个角色,如果实参是一个空表,则函数 null 返回  。

> (null nil)
T

而如果实参是逻辑 ,则函数 not 返回 真 :

> (not nil)
T

nullnot 做的是一样的事情。
在 Common Lisp 里,最简单的条件式是 if。通常接受三个实参:一个 test 表达式,一个 then 表达式和一个 else 表达式。若 test 表达式求值为逻辑  ,则对 then 表达式求值,并返回这个值。若 test 表达式求值为逻辑  ,则对 else 表达式求值,并返回这个值:

> (if (listp '(a b c))
      (+ 1 2)
      (+ 5 6))
3
> (if (listp 27)
      (+ 1 2)
      (+ 5 6))
11

quote 相同,if 是特殊的操作符。不能用函数来实现,因为实参在函数调用时永远会被求值,而 if 的特点是,只有最后两个实参的其中一个会被求值。if 的最后一个实参是选择性的。如果忽略它的话,缺省值是 nil

> (if (listp 27)
     (+ 1 2))
NIL

虽然 t 是逻辑 的缺省表示法,任何非 nil 的东西,在逻辑的上下文里通通被视为

> (if 27 1 2)
1

逻辑操作符 andor 与条件式类似。两者都接受任意数量的实参,但仅对能影响返回值的几个实参求值。如果所有的实参都为 (即非 nil ),那么 and 会返回最后一个实参的值:

> (and t (+ 1 2))
3

如果其中一个实参为 ,那之后的所有实参都不会被求值。 or 也是如此,只要碰到一个为 的实参,就停止对之后所有的实参求值。
这两个操作符称为。宏和特殊的操作符一样,可以绕过一般的求值规则。

本作品采用《CC 协议》,转载必须注明作者和本文链接
不要试图用百米冲刺的方法完成马拉松比赛。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!