首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么‘,@’的用法不像我在这个常见的lisp宏中所期望的那样工作呢?为什么Slime返回此错误消息?

为什么‘,@’的用法不像我在这个常见的lisp宏中所期望的那样工作呢?为什么Slime返回此错误消息?
EN

Stack Overflow用户
提问于 2022-04-07 19:53:16
回答 3查看 81关注 0票数 1

我正在读温斯顿写的书“Lisp”。此外,我正在使用SBCL,Emacs,和泥。

在第8章(关于宏)中,本书有以下练习:

问题8-7:堆栈是一组学习有序的东西,可以使用push和pop操作访问。我们的推送将一个新项添加到堆栈的顶部,而我们的POP则删除堆栈顶部的项。列表可以用来表示堆栈,第一个元素对应于顶部的项。将我们的推送和弹出定义为宏。我们的推送包含两个参数,要推送的项和变量的名称,变量的值为表示堆栈的列表。返回的值是扩大的列表。我们的POP只接受一个元素,即值在列表中的变量的名称。返回的值是弹出的项。在这两种情况下,变量的值都会被更改,以反映堆栈的新状态。

对于pop函数,我得到了正确的答案。然而,我在push的重新实现方面遇到了麻烦。这是答案:

代码语言:javascript
复制
(defmacro book-our-push (item-to-be-pushed stack)
  `(setq ,stack (cons ,item-to-be-pushed ,stack)))

它如预期的那样工作。我最初的回答与此相似:

代码语言:javascript
复制
(defmacro our-push (item-to-be-pushed stack)
  `(setf ,stack (list ,item-to-be-pushed ,stack)))

但是,这会生成意外的列表嵌套,如:

代码语言:javascript
复制
CL-USER> (defparameter so-stack '(3 4 5 6))
SO-STACK

CL-USER> so-stack
(3 4 5 6)

CL-USER> (our-push 2 so-stack)
(2 (3 4 5 6))

然后,我想:“哦,这一定是你使用,@的那种情况”。因为,@创建了剪接行为。因此,我做到了:

代码语言:javascript
复制
(defmacro our-push (item-to-be-pushed stack)
  `(setf ,stack (list ,item-to-be-pushed ,@stack)))

然而,它的不工作。此外,Slime还抛出了一条我觉得奇怪的错误信息:

代码语言:javascript
复制
The value
  SO-STACK
is not of type
  LIST

特别是因为:

代码语言:javascript
复制
CL-USER> (listp so-stack)

T

有办法在这种情况下使用,@吗?为什么表示变量so-stack而不是的slime实际上是一个列表?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2022-04-08 08:27:01

你已经到了工作水平。使用宏函数展开宏时,您将对源代码进行操作。

源代码是这个列表

代码语言:javascript
复制
(my-push thing foos)

也就是说,有三个符号的列表,名为my-pushthingfoos.

宏函数接受此列表并将其转换为不同的列表,在您的示例中:

代码语言:javascript
复制
(setf foos (list thing foos))

然后进一步编译(更多宏扩展,最后编译到机器代码)。

如果您试图拼接foos,这是不可能的,因为符号不是列表。

宏函数从未看到这些符号的含义。它甚至在表单被编译成机器代码之前就完成了。

票数 2
EN

Stack Overflow用户

发布于 2022-04-08 09:15:37

这是答案:

代码语言:javascript
复制
(defmacro book-our-push (item-to-be-pushed stack)
  `(setq ,stack (cons ,item-to-be-pushed ,stack)))

正如你所说,你最初的回答是相似的:

代码语言:javascript
复制
(defmacro our-push (item-to-be-pushed stack)
  `(setf ,stack (list ,item-to-be-pushed ,stack)))

本质上,这就是问题所在。cons就像一根胶水管--它把一个值粘在一个现有的列表上。list只是一个大麻袋,我可以把东西放进去。

代码语言:javascript
复制
(list 5 '(1 2 3 4)) ; puts 5 and '(1 2 3 4) into a single list, side by side
                    ; => (5 (1 2 3 4))

(cons 5 '(1 2 3 4)) ; attaches 5 to list '(1 2 3 4)
                    ; => (5 1 2 3 4)

通常,你使用列表-只是在这种情况下不使用。

然后尝试使用@将这些值拼接在一起。它没有起作用。

代码语言:javascript
复制
(defmacro our-push-splice (item-to-be-pushed stack)
  `(setf ,stack (list ,item-to-be-pushed ,@stack)))

找出它不起作用的最好方法是使用宏展开-1。这表明了宏实际上在做什么,而不是它应该做什么。

代码语言:javascript
复制
CL-USER> (defparameter stack '(1 2 3 4))
STACK
CL-USER> (macroexpand-1 '(our-push 5 stack))
(SETF STACK (LIST 5 STACK))
T
CL-USER> (macroexpand-1 '(our-push-splice 5 stack))
(SETF STACK (LIST 5 . STACK))
T
CL-USER> (our-push 5 stack)
(5 (1 2 3 4))
CL-USER> (our-push-splice 5 stack)
; Evaluation aborted on #<TYPE-ERROR expected-type: LIST datum: STACK>.

因此,剪接的尝试失败了,因为在计算宏时,似乎预期堆栈会被拼接。名字是插入的,仅此而已。

票数 1
EN

Stack Overflow用户

发布于 2022-04-08 09:26:45

Svante的回答是正确的:宏是转换源代码的函数。但有时,通过让宏打印它们正在做的事情来看到这一点是很有用的。这里有一个简单的黑客来做这个(其他人的一个更一般的黑客是这里)。

代码语言:javascript
复制
(defmacro define-traced-macro (name args &body forms)
  ;; Define a traced macro.  This probably misses edge cases
  `(progn
     (defmacro ,name ,args ,@forms)
     (let ((m (macro-function ',name)))
       (setf (macro-function ',name)
             (lambda (form environment)
               ;; Do it like this so we get the source form even if
               ;; the macro fails
               (let ((*print-pretty* t))
                 (format *trace-output* "~&~S~%" form))
               (let ((*print-pretty* t)
                     (expansion (funcall m form environment)))
                 (format *trace-output* "~& -> ~S~%" expansion)
                 expansion))))
     ',name))

现在,如果用define-traced-macro而不是defmacro定义宏,它将跟踪它的展开:

代码语言:javascript
复制
(define-traced-macro book-our-push (item-to-be-pushed stack)
  `(setq ,stack (cons ,item-to-be-pushed ,stack)))
代码语言:javascript
复制
> (let ((x '(1 2)))
    (book-our-push 3 x)
    x)
(book-our-push 3 x)
 -> (setq x (cons 3 x))
(3 1 2)

这应该能令人信服地说明为什么你的版本不能工作。

顺便说一句,一旦您拥有了define-traced-macro,您就可以使用它来重新定义自己(要认真地这样做,您可能希望通过将自己的扩展硬连接到源中来定义它):

代码语言:javascript
复制
> (define-traced-macro book-our-push (item-to-be-pushed stack)
    `(setq ,stack (cons ,item-to-be-pushed ,stack)))
(define-traced-macro book-our-push (item-to-be-pushed stack)
  `(setq ,stack (cons ,item-to-be-pushed ,stack)))
 -> (progn
      (defmacro book-our-push (item-to-be-pushed stack)
        `(setq ,stack (cons ,item-to-be-pushed ,stack)))
      (let ((m (macro-function 'book-our-push)))
        (setf (macro-function 'book-our-push)
              (lambda (form environment)
                (let ((*print-pretty* t))
                  (format *trace-output* "~&~S~%" form))
                (let ((*print-pretty* t)
                      (expansion (funcall m form environment)))
                  (format *trace-output* "~& -> ~S~%" expansion)
                  expansion))))
      'book-our-push)
book-our-push

这是很有趣的,如果这是你娱乐的那种东西。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71788178

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档