19.2 输入流

绝大多数用于读取文本的 Lisp 函数都可以接受一个 input stream 作为参数。输入流指明了,从什么地方,以及如何读取文本中字符。这里列举了一下可以作为输入流的对象类型:

buffer(缓冲区)

输入字符来自于缓冲区 buffer,从位点开始。其中位点处于待读取字符的前面一格。

marker(标记)

输入字符来自于标记 marker 所在缓冲区,从标记开始。曲中标记处于待读取字符的前面一格。将标记作为流并不会对标记的值产生副作用。

string(字符串)

输入字符来自于字符串 string,从字符串的第一个字符开始,并尽可能贪婪的读取字符。

function(函数)

输入字符由函数 function生产,这个函数必须满足以下两点:

  • 该函数被无参调用时,应当返回下一个待读取的字符。

  • 该函数被单参调用时(通常参数为单个字符),函数应该保存参数,并将其整理,用于下一次的调用。这个行为称为预读取(unreading)字符;预读取通常发生在这种情况:Lisp 读取器 多次读取单个字符,并想将其重新放回原流。这种情况下,函数返回值将不那么重要。

t

t 用于表示从 minibuffer 中读取。实际上,minibuffer 仅会被调用一次,将用户的输入处理为一个字符串,再将该字符串作为输入流。如果 Emacs 在 批处理模式(查阅 批处理模型) 下运行,那么将会使用标准输入,而不是minibuffer。例如在批处理模式下,

(message "%s" (read t))

将会从标准输入读取 Lisp 表达式,然后将结果打印到标准输出。

nil

nil 表示使用变量 standart-input 的值作为输入流;这个值通常是默认的输入流,而且必须为 非nil 输入流。

symbol

当一个符号作为输入流时,Emacs 会检查其函数定义,并将该定义作为输入流。

这里有个例子,展示了将缓冲区 buffer 作为输入流时,对位点的副作用:

---------- Buffer: foo ----------
This∗ is the contents of foo.
---------- Buffer: foo ----------

(read (get-buffer "foo"))
     ⇒ is
(read (get-buffer "foo"))
     ⇒ the

---------- Buffer: foo ----------
This is the∗ contents of foo.
---------- Buffer: foo ----------

请注意,第一次读取时,跳过了一个空格。读取会跳过关键文本前的适量空格。

这里有一个将标记 marker 作为输入流的例子,其读取到的值为一个符号 This。

---------- Buffer: foo ----------
This is the contents of foo.
---------- Buffer: foo ----------

(setq m (set-marker (make-marker) 1 (get-buffer "foo")))
     ⇒ #<marker at 1 in foo>
(read m)
     ⇒ This
m
     ⇒ #<marker at 5 in foo>   ;; Before the first space.

这里例子里,我们读取一个字符串:

(read "(When in) the course")
     ⇒ (When in)

下面这个例子,我们从 minibuffer 中读取内容。其中" Lisp expression:"是提示语。(当你从流 t 中读取时,总是会有提示语)用户的输入紧跟在提示语。

(read t)
     ⇒ 23
---------- Buffer: Minibuffer ----------
Lisp expression: 23 RET
---------- Buffer: Minibuffer ----------

最后这个例子中,我们从将函数作为输入流。这个函数名称为 useless-stream。在读取前,我们需要初始化变量 useless-list。之后每一次对函数 useless-steam的调用,都会从该 变量 中获取下一个字符。

(setq useless-list (append "XY()" nil))
     ⇒ (88 89 40 41)

(defun useless-stream (&optional unread)
  (if unread
      (setq useless-list (cons unread useless-list))
    (prog1 (car useless-list)
           (setq useless-list (cdr useless-list)))))
     ⇒ useless-stream

现在,让我们从流中读取数据:

(read 'useless-stream)
     ⇒ XY
useless-list
     ⇒ (40 41)

请注意,开闭括号仍在 list 中。Lisp 读取器会对开括号进行计数,以此判断输入何时结束,并停止读取。你可以尝试另一个例子,读取"()"将会返回nil。

最后更新于