Skip to content

LISP - 交互式 I/O

交互式 I/O 是 Lisp 开发体验的核心,其典型代表是 REPL(读取-求值-打印循环)。本章将介绍处理 I/O 的现代方法,从与用户交互到稳健地管理文件。

REPL 不仅仅是一个命令行;它是一个强大、动态的开发环境。典型的工作流程是:

  • 读取(Read):REPL 读取您输入的 Lisp 表达式。
  • 求值(Eval):它求值(执行)该表达式。
  • 打印(Print):它将求值结果打印回给您。
  • 循环(Loop):它等待您的下一次输入。

现代 Lisp 环境,如 SLIME(适用于 Emacs)或 SLY(适用于 Emacs/VS Code),极大地增强了 REPL 的功能,允许您交互式地编译文件、检查对象和调试代码。

流(Stream) 是 Lisp 中 I/O 的基本抽象。它代表数据的来源或目的地。Common Lisp 提供了几个标准流以方便使用:

  • *standard-input*:默认输入流,通常连接到键盘。
  • *standard-output*:默认输出流,通常是终端显示器。
  • *error-output*:用于错误消息的流,通常也是终端。
  • *terminal-io*:一个特殊的双向流,用于与用户直接交互。
  • read:从流中读取一个完整的 Lisp 表达式(S-表达式)。它将文本解析为 Lisp 对象。
  • read-line:将一行文本作为字符串读取,并消耗换行符。
  • read-char:从流中读取一个字符。
  • print:打印一个 Lisp 对象,前面有一个换行符,后面有一个空格。设计为机器可读(可以使用 read 再次读取)。
  • prin1:类似于 print,但没有换行符和空格。
  • princ:以更易于人类阅读的格式打印对象(例如,不带引号的字符串)。
  • write:一个高度可配置的打印对象函数。
  • format:最强大的输出函数。它使用控制指令创建格式化字符串。

format 是一个多功能的工具,用于生成结构化文本。它的第一个参数是目标流(t 表示 *standard-output*,nil 表示返回一个字符串),后面跟着一个控制字符串和参数。

;; `format` 使用示例
(defparameter *user-name* "Alex")
(defparameter *items-count* 5)
;; 打印到标准输出
(format t "Hello, ~a! You have ~d items.~%" *user-name* *items-count*)
;; 使用的指令:
;; ~a : 美学(人类可读)表示
;; ~d : 十进制整数
;; ~% : 换行符
;; 使用 nil 作为目标创建格式化字符串
(defparameter *message*
(format nil "Report for user: ~s" *user-name*))
(print *message*)
;; ~s : 标准(机器可读)表示
Hello, Alex! You have 5 items.
"Report for user: \"Alex\""

虽然 open 和 close 存在,但处理文件的现代且安全的方式是使用 with-open-file 宏。即使在其主体中发生错误,它也会自动处理文件流的关闭。这可以防止资源泄漏。

(with-open-file (stream-variable filespec options*)
;; 使用 stream-variable 的代码
)
;; --- 写入文件 ---
(with-open-file (out "/tmp/my-data.txt"
:direction :output
:if-exists :supersede) ;; 如果文件存在则覆盖
(format out "This is line 1.~%")
(format out "This is line 2.~%"))
(format t "File 'my-data.txt' written successfully.~%~%")
;; --- 读取文件 ---
(format t "Contents of 'my-data.txt':~%")
(with-open-file (in "/tmp/my-data.txt" :direction :input)
(loop for line = (read-line in nil) ;; 读取行,文件结束时返回 nil
while line
do (format t "> ~a~%" line)))
File 'my-data.txt' written successfully.
Contents of 'my-data.txt':
> This is line 1.
> This is line 2.

对于实际项目,您不会只在 REPL 中输入代码。您会将代码组织成文件和项目。标准工具包括:

  • ASDF(Another System Definition Facility,另一个系统定义工具):Common Lisp 事实上的构建系统。您通过定义 .asd 文件来指定哪些文件属于您的项目(或“系统”)。
  • Quicklisp:Common Lisp 事实上的库管理器。它允许您轻松下载、安装和加载数千个开源库。