按语:这是我在萧萧的林间为「不懂编程的人」写的系列文章的第四篇,整理于此。它的前一篇是《勤劳,还是懒惰?》,讲述了编程的目的在于让自己或者别人更省力地完成一些重复性的劳动。从这篇文章开始,正式学习 Emacs Lisp 编程方面的知识。

到现在为止,我们已经大致见识了在 Emacs Lisp 语言中如何定义一个函数,也见识了interactiveinsertread-stringlist 等函数的基本用法。我几乎未对这些函数给出细致的解释,但是通过几次简单的实践,它们的功能应该是显而易见的。不过,这种显而易见是建立在对编程的基本知识有所了解的基础之上的。

(list (read-string "变量名称?")
      (read-string "类型?")
      (read-string "数量?"))

为例,这个表达式是什么意思呢?

倘若将 (read-string "变量名称?")(read-string "类型?")(read-string "数量?") 分别视为 xyz,那么上述代码可简化为:

(list x y z)

这行代码揭示的是,list 是一个函数,它接受三个参数。事实上,它可以接受无限多个参数,只要计算机的内存空间足够。

list 函数的作用是让它的所有参数排队,从左向右,先来后到,形成一个序列。这种序列称为列表。list 函数的参数可分为两类,一类是原子,另一类是列表。

不仅 list 函数能如此,所有函数皆能如此。

原子

我们现今探索到的世界真相,有一条最基本的原理:物质由原子构成。化学家相信,化学反应中的最小单位是原子。

对于一个程序而言,它的输入数据与输出数据,以及程序本身,这一切也皆由原子构成。

以上文里的 list 函数所接受的参数 x 为例,当这个参数的值为 (read-string "变量名称?") 时,要想从这种表象中看到原子,需要不断的剥离或穿透由表达式构成的外衣。

(read-string "变量名称?") 是一个表达式,在 Emacs Lisp 解释器看来,它是一个函数求值表达式,亦即 read-string 是一个函数,现在它接受了一个参数 "变量名称?"。现在,你已经看到了一个原子,就是这个参数。这个原子,名叫字符串。

read-string 函数在接受这个参数之后,会将它显示于 Emacs 的微缓冲区,然后它等待接收着我们在微缓冲区里的输入。当我输入 foo <RET> 的时候,Emacs 会将我输入的 foo 变成字符串原子传给 read-string,而 read-string 会将这个原子作为它的求值结果。

因此,Emacs Lisp 解释器对 (read-string "变量名称?") 的求值,最终得到的是一个字符串。

字符串只是 Emacs Lisp 语言里的一种原子。不要小看它,古今中外,任何一部文学著作皆由这种原子构成。在 Emacs 的缓冲区,它的本体也不过是一个字符串原子。

除了字符串之外,Emacs Lisp 还提供了符号、数字、布尔值(真值 t 与假值 nil)、向量这些原子。有关它们的一些概念与用法,在编程的过程中遇到它们时再作介绍。因为这部分知识单独拿出来,逐一细说,不仅无趣,而且无趣。

列表

在化学家看来,分子由原子构成。在 Emacs Lisp 中,列表也由原子构成。因此,不妨将列表当成一种分子。虽然以前我们没有学过编程,但是却学过一点点化学知识。

列表是一种很重要的分子。因为列表不仅可由原子构成,也可以由列表构成,还可以由原子与列表混合构成。由列表或者由原子与列表混合构成的列表,它们可以构成各种各样的分子,例如环状的、树状的、表格状的、图状的……不妨将列表称为元分子。

(list x y z) 可以构成列表 (x y z),亦即 Emacs Lisp 解释器对这个表达式的求值结果是 (x y z)。可以通过一个小试验证实这一点。

用 Emacs 打开 init.el 文件,然后在文件末尾增加一行代码:

(list "foo" "double" "n")

然后,将光标移动到这行代码的尾部,然后向 Emacs 发送指令 C-x C-e,然后 Emacs Lisp 解释器便会对这行代码(表达式)求值,求值结果显现于微缓冲区,即:

("foo" "double" "n")

这是一个由三个字符串原子构成的列表。

注:光标,就是在 Emacs 窗口中某处一闪一闪的矩形块,表示在这个位置上可以插入文本。

现在,再在 init.el 文件的末尾增加一行代码:

(list "foo" "double" "n" (list "foo" "double" "n"))

再用 C-x C-e 对这行代码求值,结果为:

("foo" "double" "n" ("foo" "double" "n"))

这个结果依然是一个列表,但它是由三个字符串原子与一个列表构成。

符号原子

现在,我们知道了表达式 (list "foo" "double" "n") 的求值结果是列表 ("foo" "double" "n"),但是认真看一下这个表达式与它的求值结果,可以发现它们的形状一样。Emacs Lisp 解释器如何区分一个可求值的表达式与一个列表呢?

(list "foo" "double" "n") 其实也是一个列表,只不过它与 ("foo" "double" "n") 的不同之处在于它的第一个元素(不是化学里的元素,而是数学集合论中的元素,将列表视为集合),不是字符串,不是数字,不是布尔值,也不是向量,它是一个符号,与 +-*/ 这些数学运算符号没有本质上的区别。

例如,要计算 1 + 2,用 Emacs Lisp 语言,可将其描述为:

(+ 1 2)

这里的 + 是符号,12 是数字原子,这三者构成了一个列表,但 Emacs Lisp 解释器会认为这是个函数求值表达式。没错,在 Emacs Lisp 中,+ 也是一个函数。

符号是一种原子。当列表的第一个元素是符号原子时,Emacs Lisp 解释器就会认为这是一个函数求值表达式。

在 Emacs Lisp 符号原子有许多用途,但是它的主要用途是作为函数的名字,并且作为 Emacs Lisp 判定一个表达式是否为函数求值表达式的特征。

现在,我问你一个问题,

(defun hello-world ()
  (interactive)
  (insert "hello, world"))

这块代码,是不是一个列表?

虚空

古希腊哲学家德谟克利特说,万物的本原是原子与虚空。原子是不可再分的物质微粒,虚空是原子运动的场所。

我们已经大致见识了 Emacs Lisp 中的一些原子,那么对于由原子构成的程序而言,虚空是什么?

是 Emacs Lisp 解释器。

由于我坚信 Emacs 是一台计算机,那么虚空就是这台计算机的 CPU。

倘若你现在认为自己不过是一个程序,自己的身体由一堆原子构成,那么这个宇宙必定有一个 CPU 之类的东西存在……为了增加一点东方神秘气息,打开《道德经》,应该能找到对这个 CPU 的描述:有物混成,先天地生。寂兮寥兮,独立不改,周行而不殆,可以为天地母。

请不要想太多,否则……后果自负。

下一篇周而复始

你可能感兴趣的文章

birdshark · 2017年12月05日

玄学?

回复

0

可否说说玄在何处

garfileo 作者 · 2017年12月05日
载入中...
garfileo garfileo

4.7k 声望

发布于专栏

while(1) { }

只关心 C 与 Lisp/Scheme 程序的基本形状,其他的事关心不过来。

136 人关注