前一篇《 第一声问候》讲述了如何用 Emacs Lisp 写一个可以在 Emacs 计算机里运行的 hello world 程序。
我们已经通过一个极为简单的 hello-world
函数,向 Emacs 计算机发出了第一声问候。
这个 hello-world
函数的定义非常简单:
(defun hello-world ()
(interactive)
(insert "hello, world"))
但是一旦弄懂了如何使用 Emacs Lisp 语言定义一个函数,以及 interactive
与 insert
的用法,我们就可以在很多时候从一个勤劳的人蜕变为一个懒惰的人。
此前,有一段时间,我经常用 POV-Ray [1] 渲染一些很简单的三维场景:我向 POV-Ray 提供场景文件,由 POV-Ray 使用光线追踪技术基于场景文件中的描述完成三维渲染。每份 POV-Ray 场景描述文件往往是以下面内容作为开始:
#version 3.7;
#include "colors.inc"
global_settings {
assumed_gamma 1.0
}
一开始我比较勤劳,会在每份场景文件中很认真地手动输入上述内容。现在就可以不必如此,在 init.el 中像下面这样定义一个函数:
(defun povray-head ()
(interactive)
(insert
"#version 3.7\n"
"#include \"colors.inc\"\n\n"
"global_settings {\n"
" assumed_gamma 1.0\n"
"}\n"))
这样,每次当我新建了一份 POV-Ray 场景文件之后,只需要向 Emacs 发送 M-x povray-head
指令,场景文件中就会自动出现上述内容。
看,并不需要多么高超的编程技术,只是将 hello-world
函数略微改动一下,就节省了许多重复性的劳动。从物理学的角度来看,在我编写许多 POV-Ray 场景描述文件时,这个 povray-head
函数可以让我更省力。
所以,在我看来一个程序首先应该是一种有用的机械,它与杠杆、滑轮、斜面这些基本的省力装置不应该存在本质上的区别。因此,在写一个程序之前,需要明确,这个程序是否可以让你或让其他使用这个程序的人更省力。不省力的程序,是没必要去写的。
最近为了写几篇 Emacs Lisp 编程方面的文章,我不得不在 Emacs 里编程。实际上我也经常需要使用 C 语言在 Emacs 的宿主系统中编程。我经常需要写一些像下面这样的代码:
double *foo = malloc(n * sizeof(double));
若没学过 C 语言,就不必在意这行代码的含义,将它视为普通的文本即可。倘若手写这行代码,我必须写两次 double
。现在,我可以再在 init.el 中定义一个 c-malloc
函数,让我在写此类代码时能够省点力。
以下是 c-malloc
的定义:
(defun c-malloc (name type n)
(interactive
(list (read-string "变量名称?")
(read-string "类型?")
(read-string "数量?")))
(insert (format "%s *%s = malloc(%s * sizeof(%s));" type name n type)))
这个函数的定义,与 hello-world
以及 povray-head
不一样,它是带有参数的函数。与后两者相比,它更符合我们在数学中对函数建立的一般性认识,即 w = f(x, y, z) 或 f: (x, y, z) -> w。
暂时不考虑 c-malloc
的细节,先看它是如何工作的。
现在,倘若 Emacs 重新加载了 init.el 文件,那么使用 M-x c-malloc
指令便可让 Emacs Lisp 解释器对 c-malloc
进行求值,但是 Emacs 在执行上述指令后,会在微缓冲区里问我三个问题。
第一个问题是:
变量名称?
我(在微缓冲区里)输入指令 foo
,就回答了这个问题。紧接着,Emacs 会再问:
类型?
我输入指令 double
,就回答了这个问题。最后,Emacs 会问:
数量?
我回答 n
。
注:表示单击回车键。例如
n
,表示输入n
之后,单击回车键。
回答了这三个问题之后,Emacs 就会在缓冲区里显现:
double *foo = malloc(n * sizeof(double));
实际上,Emacs 接受我的答案之后,会将它们依序分别绑定到 c-malloc
的三个参数 name
、type
、n
上,这就相当于形成了下面的表达式:
(c-malloc "foo" "double" "n")
接下来,Emacs Lisp 解释器对这个表达式进行求值,结果就是在缓冲区里插入
double *foo = malloc(n * sizeof(double));
像这些琐碎但是出现频率比较高的代码片段,花一点时间,用编程的方式来生成它们,不仅省力,而且也避免了手工输入的错误。一个勤劳的人,应该用编程的方式让自己变懒,而不是让自己活成一段程序。
上文中出现的 Emacs Lisp 代码,看不懂没有关系,因为在下一篇就开始讲述 Emacs Lisp 的文本处理方面的编程知识。
下一篇: 原子与虚空
[1] http://povray.org/