Common Lisp 执行表达式作为宏中的参数
Common Lisp Execute expression as parameter in macro
所以使用普通的 lisp,我希望能够做一些事情:
(defmacro foo (count &rest someExpression)
`(do
((,count 0 (+ ,count 1)))
((= ,count 5) T)
`(eval ,someExpression)
)
)
(foo (print 1) temp)
打印1 5 次的结果。我不想直接调用 (print 1),而是通过宏参数传递表达式并通过宏调用它。换句话说,宏 foo 应该处理任何表达式作为输入并且 运行 它。这个案例好像不行。
已编辑以阐明明确的脚本和预期功能。
从你最近的版本开始,它至少是一个与旧版本不同的宏的合理候选者:
(defmacro foo (someExpression count-var)
`(do ((,count-var 0 (+ ,count 1)))
((= ,count-var 5) T)
`(eval (,someExpression))))
那么(foo (print 1) c)
的扩展是什么?
(foo (print 1) x)
-> (do ((x 0 (+ x 1))) ((= x 5) t)
`(eval (,someexpression)))
好吧,那是一场灾难:那个嵌套的反引号在做什么?让我们删除它:
(defmacro foo (someExpression count-var)
`(do ((,count-var 0 (+ ,count 1)))
((= ,count-var 5) T)
(eval (,someExpression))))
(foo (print 1) x)
-> (do ((x 0 (+ x 1))) ((= x 5) t)
(eval ((print 1))))
这没有那么严重,但是 eval
表格完全是伪造的。我们可以通过将其更改为至少在语法上合法来使 'work':
(defmacro foo (someExpression count)
`(do ((,count 0 (+ ,count 1)))
((= ,count 5) T)
(eval ,someExpression)))
现在
(foo (print 1) x)
-> (do ((x 0 (+ x 1))) ((= x 5) t)
(eval (print 1)))
这会 'work' 但这纯粹是巧合:因为 (print 1)
returns 1
而 1
的值是 1
.
(foo (print 'foo) x)
-> (do ((x 0 (+ x 1))) ((= x 5) t)
(eval (print 'foo)))
这是一个 运行 时间错误。
但是...您为什么要使用 eval
? eval
对几乎所有你能想到的问题来说都是一个糟糕透顶的解决方案,除非问题的解决方案被称为 'code injection attack',在这种情况下,它不仅糟糕:它是错误的。所以我们只是删除它。
(defmacro foo (someExpression count)
`(do ((,count 0 (+ ,count 1)))
((= ,count 5) T)
,someExpression))
现在
(foo (print 'foo) x)
-> (do ((x 0 (+ x 1))) ((= x 5) t)
(print 'foo))
这看起来像我们想要的代码转换。所以,最后:
> (foo (print 'foo) x)
foo
foo
foo
foo
foo
t
这终于好了。这有效:
> (foo (print x) x)
0
1
2
3
4
t
与对问题的另一个编辑一样,将变量名放在第一位并允许使用一堆表达式可能更有用:
(defmacro foo (count-var &body forms)
`(do ((,count-var 0 (+ ,count-var 1)))
((= ,count-var 5))
,@forms))
这将允许在正文中使用多个表达式。我们可以更进一步:我们可以允许它指定迭代次数和 return 值`:
(defmacro foo ((count-var &optional (count 1) (value 'nil)) &body forms)
`(do ((,count-var 0 (1+ ,count-var)))
((= ,count-var ,count) ,value)
,@forms))
现在
> (foo (x 2)
(print x)
(print (* x 2)))
0
0
1
2
nil
嗯,这个宏的名字当然是dotimes
所以使用普通的 lisp,我希望能够做一些事情:
(defmacro foo (count &rest someExpression)
`(do
((,count 0 (+ ,count 1)))
((= ,count 5) T)
`(eval ,someExpression)
)
)
(foo (print 1) temp)
打印1 5 次的结果。我不想直接调用 (print 1),而是通过宏参数传递表达式并通过宏调用它。换句话说,宏 foo 应该处理任何表达式作为输入并且 运行 它。这个案例好像不行。
已编辑以阐明明确的脚本和预期功能。
从你最近的版本开始,它至少是一个与旧版本不同的宏的合理候选者:
(defmacro foo (someExpression count-var)
`(do ((,count-var 0 (+ ,count 1)))
((= ,count-var 5) T)
`(eval (,someExpression))))
那么(foo (print 1) c)
的扩展是什么?
(foo (print 1) x)
-> (do ((x 0 (+ x 1))) ((= x 5) t)
`(eval (,someexpression)))
好吧,那是一场灾难:那个嵌套的反引号在做什么?让我们删除它:
(defmacro foo (someExpression count-var)
`(do ((,count-var 0 (+ ,count 1)))
((= ,count-var 5) T)
(eval (,someExpression))))
(foo (print 1) x)
-> (do ((x 0 (+ x 1))) ((= x 5) t)
(eval ((print 1))))
这没有那么严重,但是 eval
表格完全是伪造的。我们可以通过将其更改为至少在语法上合法来使 'work':
(defmacro foo (someExpression count)
`(do ((,count 0 (+ ,count 1)))
((= ,count 5) T)
(eval ,someExpression)))
现在
(foo (print 1) x)
-> (do ((x 0 (+ x 1))) ((= x 5) t)
(eval (print 1)))
这会 'work' 但这纯粹是巧合:因为 (print 1)
returns 1
而 1
的值是 1
.
(foo (print 'foo) x)
-> (do ((x 0 (+ x 1))) ((= x 5) t)
(eval (print 'foo)))
这是一个 运行 时间错误。
但是...您为什么要使用 eval
? eval
对几乎所有你能想到的问题来说都是一个糟糕透顶的解决方案,除非问题的解决方案被称为 'code injection attack',在这种情况下,它不仅糟糕:它是错误的。所以我们只是删除它。
(defmacro foo (someExpression count)
`(do ((,count 0 (+ ,count 1)))
((= ,count 5) T)
,someExpression))
现在
(foo (print 'foo) x)
-> (do ((x 0 (+ x 1))) ((= x 5) t)
(print 'foo))
这看起来像我们想要的代码转换。所以,最后:
> (foo (print 'foo) x)
foo
foo
foo
foo
foo
t
这终于好了。这有效:
> (foo (print x) x)
0
1
2
3
4
t
与对问题的另一个编辑一样,将变量名放在第一位并允许使用一堆表达式可能更有用:
(defmacro foo (count-var &body forms)
`(do ((,count-var 0 (+ ,count-var 1)))
((= ,count-var 5))
,@forms))
这将允许在正文中使用多个表达式。我们可以更进一步:我们可以允许它指定迭代次数和 return 值`:
(defmacro foo ((count-var &optional (count 1) (value 'nil)) &body forms)
`(do ((,count-var 0 (1+ ,count-var)))
((= ,count-var ,count) ,value)
,@forms))
现在
> (foo (x 2)
(print x)
(print (* x 2)))
0
0
1
2
nil
嗯,这个宏的名字当然是dotimes