Skip to content

LISP - 程序结构

Lisp 程序由 S-表达式(Symbolic Expressions)构建。S-表达式是嵌套列表数据结构的文本表示形式。在 Lisp 中,代码即数据,S-表达式就是用来表示代码的数据结构。S-表达式的基本组成部分是原子(atoms)(如数字 42、符号 my-variable 和字符串 "hello")和列表(lists)(原子或其他列表的序列,用括号括起来,如 (+ 2 3))。

Lisp 程序通常使用 REPL(Read-Eval-Print Loop,读取-求值-打印循环)进行交互式开发。现代的 Common Lisp 实现,如 SBCL (Steel Bank Common Lisp) 或 CCL (Clozure CL),是高性能编译器,它们会即时编译代码,让您在拥有解释器体验的同时享受到编译代码的速度。

REPL 是交互式 Lisp 开发的核心。它执行一个简单而强大的循环:

  1. 读取(Read):它读取您在提示符下输入的 S-表达式。
  2. 求值(Eval):它对 S-表达式进行求值,将其作为代码执行。
  3. 打印(Print):它将求值结果打印回给您。
  4. 循环(Loop):它等待您的下一次输入。

让我们编写一个 S-表达式来找出三个数字 7、9 和 11 的和。在 REPL 提示符下(通常是 *),您键入以下 S-表达式:

;; '+' 是函数,数字是它的参数。
(+ 7 9 11)

在您按下 Enter 键后,REPL 会对其进行求值并打印结果:

27

如您所见,Lisp 使用前缀表示法(也称为波兰表示法)。运算符或函数名总是位于括号内的首位,后跟其参数。这是整个语言(从算术到复杂操作)中的一个一致规则。

例如,标准的中缀数学表达式 (10 - 4) * 3 在 Lisp 的前缀表示法中将写为:

;; 前缀表示法:运算符在前,操作数在后。
(* (- 10 4) 3)

让我们看另一个例子:将 68°F 的温度转换为摄氏度。公式是 C = (F - 32) * 5/9。

;; 华氏度到摄氏度转换的 Lisp 代码。
;; 请注意,5/9 会生成一个有理数,确保精度。
(* (- 68 32) 5/9)

REPL 会对此进行求值并返回:

20

对于大型程序,您会将代码写入源文件中,通常以 .lisp 为扩展名。然后您可以将此文件加载到正在运行的 Lisp 环境中或对其进行编译。

;;; main.lisp - 一个简单的 Lisp 程序示例。
;; 定义一个将华氏度转换为摄氏度的函数。
(defun fahrenheit-to-celsius (temp-f)
"将华氏温度转换为摄氏温度。"
(* (- temp-f 32) 5/9))
;; 调用函数并使用 FORMAT 打印结果。
;; ~a 是值的“美观表示”的占位符。
;; ~% 添加一个换行符。
(format t "68F is ~aC.~%" (fahrenheit-to-celsius 68))

您可以从 Lisp 实现中加载并运行此文件。例如,在 REPL 中:

(load "main.lisp")

执行该文件将产生以下输出:

68F is 20C.

任何编程入门教程都离不开“Hello, World!”示例。以下是在 Common Lisp 中实现它的现代、惯用方式。

;; 使用 format 函数打印到标准输出流 't'。
;; ~% 是换行符的指令。
(format t "Hello, World!~%")
(format t "I am learning modern Common Lisp!~%")

当加载并执行时,这段代码会产生:

Hello, World!
I am learning modern Common Lisp!

要开始现代 Lisp 开发,请考虑使用集成环境。流行的选择包括:

  • Portacle:一个便携的、多平台的 Common Lisp 环境,预配置了 Emacs、SBCL 和 Quicklisp。
  • Emacs + SLIME/Sly:用于交互式 Lisp 开发的经典且最强大的设置。
  • VS Code with Alive:一个现代扩展,为 Visual Studio Code 带来了类似 SLIME 的交互式体验。

设置 Quicklisp(Common Lisp 事实上的包管理器)对于使用第三方库至关重要。