Common Lisp:如何使用条件拼接在宏中构建列表?

发布于 2025-01-03 16:43:25 字数 466 浏览 1 评论 0原文

让我们假设:

(defmacro testing (&optional var)
    `(list 'this 'is  
       ,@(when (consp var) `('a 'list))))

当被调用时:

>(testing 2)
(THIS IS)

>(testing (list 1 2))
(THIS IS A LIST)

这就是我想要的。但是现在,当我传递一个列表参数时:

>(defparameter bla (list 1 2 3))
BLA
>(testing bla)
(THIS IS)

我想这是因为宏会检查 (consp bla) 其中 bla 是一个符号,而不是列表?我该如何防止这种情况?

谢谢

Let's assume:

(defmacro testing (&optional var)
    `(list 'this 'is  
       ,@(when (consp var) `('a 'list))))

when called:

>(testing 2)
(THIS IS)

>(testing (list 1 2))
(THIS IS A LIST)

which is what I wanted. But now, when I pass a parameter that is a list:

>(defparameter bla (list 1 2 3))
BLA
>(testing bla)
(THIS IS)

which I suppose is because the macro would check (consp bla) where bla is a symbol, instead of the list? How do I prevent this?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

痴者 2025-01-10 16:43:25

你可以这样做:

(defmacro testing (&optional var)
   `(if (consp ,var)
        '(this is a list)
        '(this is)))

所以 var 将在运行时(而不是编译时)进行评估。 var 在宏的扩展中只出现一次,但如果出现多次,则必须使用 gensym。

编辑:如果您不想输入 '(this is) 两次,请执行以下操作:

(defmacro testing (&optional var)
  `(append '(this is) (when (consp ,var) '(a list))))

不要使用 eval,它很慢,而且完全没有必要。通过将 var 替换到宏扩展中,它自然会在运行时被评估。如果您使用 eval,您将执行如下操作:

(eval (append '(list 'this 'is) (when (consp 'bla) '('a 'list))))

每次执行时,它都会构建一个表示代码的列表,并在运行之前对其进行编译。 (希望这不是循环!)如果您只使用生成简单代码的宏(没有 eval),它只会编译一次。

You could do something like:

(defmacro testing (&optional var)
   `(if (consp ,var)
        '(this is a list)
        '(this is)))

So var will be evaluated at run time (not compile time). var only appears one time in the expansion of the macro, but if it appeared more than once, you would have to use a gensym.

EDIT: If you don't want to type '(this is) twice, do this:

(defmacro testing (&optional var)
  `(append '(this is) (when (consp ,var) '(a list))))

Don't use eval, it's slow, and completely unnecessary. By substituting var into the macro expansion, it will naturally be evaluated at run-time. If you use eval, you will be doing something like this:

(eval (append '(list 'this 'is) (when (consp 'bla) '('a 'list))))

Every time that executes, it will build up a list representing the code and compile it before running it. (Hopefully this isn't in a loop!) If you just use a macro which generates straightforward code (without eval), it will compile only once.

甜嗑 2025-01-10 16:43:25

这里的问题是,

,@(when (consp var) `('a 'list))))

当您只有参数的文字(未计算)值时,表达式是在编译时计算的。在您的情况下:2(list 1 2)bla

据我所知,唯一的解决方案是使用eval。这个特定的例子可以改变如下:

(defmacro testing (&optional var)
  `(eval (append '(list 'this 'is)  
                 (when (consp ',var)
                   '('a 'list))))

但是,我想,你会同意,它真的很难看。如果你想使用词法变量,它就不起作用。通常,有一些方法可以重新表述问题,这样就不需要这样的扭曲。

The problem here is that the expression

,@(when (consp var) `('a 'list))))

is evaluated at compile time, when you only have literal (unevaluated) values of arguments. In your case: 2, (list 1 2), and bla.

The only solution to this, that I'm aware of, is to use eval. This particular example can be changed as follows:

(defmacro testing (&optional var)
  `(eval (append '(list 'this 'is)  
                 (when (consp ',var)
                   '('a 'list))))

But, I think, you'll agree, that its really ugly. And it won't work if you want to use lexical variables. Usually, there are ways to reformulate the problem, so that such perversions aren't needed.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文