如何在 Lisp 中创建一个局部字符串变量?

How to make a local string variable in Lisp?

我正在尝试创建一个本地字符串变量并使用变量构建字符串。

(setq string-label (make-array 0 :element-type `character
                                 :fill-pointer 0
                                 :adjustable t))
(loop while (and (char/= char #\Space)
                 (char/= char #\()
                 (char/= char #\Newline))
      do
         (vector-push-extend char string-label)
         (setq char (read-char fstream nil)))

我遇到一个错误:

*** - SETQ: variable STRING_LABEL has no value

我假设您 运行 的代码与您提供的代码不同,因为该代码中没有对 string_label 的引用。

但是,您所展示的代码还有很多其他问题值得修复。这些本质上是

  • 你不应该通过给变量赋值来绑定变量,更不用说局部变量了,事实上这样做在 CL 中是不合法的——相反你应该使用像 let 这样的绑定形式(在下面的代码中我正在使用 loop 为我进行绑定);
  • 您在绑定之前使用 char(即便如此,它实际上并未绑定,因为您只是使用 setq);
  • 读取标签的终止条件不正确——如果你点击 EOF,你会得到一个错误,因为 char 将是 nil

此外,您的代码中存在一些不明确的地方:当您点击 'stopper' 字符之一时,是否要将其保留为要读取的下一个字符?我的版本假设你这样做。当然,这对函数的连续调用有影响:您需要向前跳过塞子。

所以,这里是您的代码的清理版本,以函数的形式呈现。这也使 'stopper' 个字符的列表成为函数的参数,并使用 member 知道何时停止(member 默认使用 eql,而 eql 与字符上的 char= 相同,所以这很好)。

(defun read-string-label (fstream &key (stoppers '(#\Space #\( #\Newline)))
  (loop with string-label = (make-array 0 :element-type 'character
                                        :fill-pointer 0
                                        :adjustable t)
        for char =  (read-char fstream nil)
        while (and char (not (member char stoppers)))
        do (vector-push-extend char string-label)
        finally (progn
                  (when char
                    ;; push the stopper character back into the stream
                    ;; so it's the next thing we read
                    (unread-char char fstream))
                  (return string-label))))

定义局部变量的主要结构是letlet*。从 let 派生的其他运算符也绑定局部变量,例如 with-open-filedestructuring-bind。当然 lambda 和从它派生的函数定义形式,如 defun 都有参数,那些是局部变量。 (从历史上看,lambdalet 之前存在)。

loop 构造中,可以使用 with 子句定义局部变量,如:

;; Like a hidden (let ((x 42) ...) in the code generated by loop:

(loop with x = 42 ...)

这允许 loop 的许多实例避免被额外的 let 包围,从而显得更多 "tidy".

局部变量也可以自动跨越 loop 的迭代,在许多情况下,如果逻辑正确,这可以避免合并笨重的 setq 赋值形式:

;; x is initially 1, then 2, 3, ...

(loop with x = 1 then (+ 1 x) ...)

Common Lisp 中的全局变量是使用 defvardefparameter 定义的,而不是简单地用 setfsetq.

赋值

简单地对以前未定义的变量发出 setfsetq 的效果受到 ANSI Lisp 不幸的 "gray area" 的影响,并且在不同的情况下表现不同由于对该灰色区域的不同解释而导致的实现。

在某些实现中,setq 对未定义的变量产生诊断。在其他情况下,它会创建一个特殊变量,类似于 defparameter,而在其他情况下,它会创建类似于 "global lexical" 变量的内容:该符号接收绑定,但未标记为特殊变量,仍然可用于词汇绑定。