【问题标题】:How to make a local string variable in Lisp?如何在 Lisp 中创建本地字符串变量?
【发布时间】:2020-02-18 05:19:18
【问题描述】:

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

(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 有一个下划线。这是哪里来的?
  • make-string。示例:(make-string 8 :initial-element #\o)。还有make-stringfill <sequence> <character>
  • STRING_LABEL 是一个错字。它应该是字符串标签。谢谢。

标签: string lisp common-lisp


【解决方案1】:

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

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

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

(loop with x = 42 ...)

这允许loop 的许多实例避免被额外的let 包围,从而显得更“整洁”。

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

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

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

Common Lisp 中的全局变量是使用defvardefparameter 定义的,而不是简单地使用setfsetq 进行赋值。

对以前未定义的变量简单地发出 setfsetq 的效果受制于 ANSI Lisp 的不幸“灰色区域”,并且由于对此的不同解释而在不同的实现上表现不同灰色地带。

在某些实现中,未定义变量上的setq 会产生诊断信息。在其他情况下,它会创建一个特殊变量,类似于defparameter,在其他情况下,它会创建类似于“全局词法”变量的东西:符号接收绑定,但没有标记为特殊变量,仍可用于词法绑定。

【讨论】:

    【解决方案2】:

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

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

    • 你不应该绑定变量,更不用说局部变量,通过分配给它们,事实上这样做在 CL 中是不合法的——相反你应该使用像 let 这样的绑定形式(在下面的代码中,我正在使用loop为我做绑定);
    • 你在绑定之前使用char(即使那样,它实际上并没有绑定,因为你只是使用setq);
    • 读取标签的终止条件不正确 - 如果您点击 EOF,您将收到错误消息,因为 char 将变为 nil

    此外,您的代码中还有一些不明确之处:当您点击“停止”字符之一时,您是否要将其作为下一个要读取的字符?我的版本假设你这样做。当然,这对函数的连续调用有影响:您需要向前跳过停止器。

    所以,这里是您的代码的清理版本,以函数的形式呈现。这也使'stopper'字符列表成为函数的参数,并使用member 知道何时停止(member 默认使用eqleql 与字符上的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))))
    

    【讨论】:

      猜你喜欢
      • 2011-07-15
      • 1970-01-01
      • 1970-01-01
      • 2012-06-12
      • 1970-01-01
      • 1970-01-01
      • 2011-03-10
      • 2014-03-03
      • 2019-12-02
      相关资源
      最近更新 更多