【发布时间】:2018-11-21 05:41:39
【问题描述】:
(defmacro random-choice (&rest exprs)
`(case (random ,(length exprs))
,@(let ((key -1))
(mapcar #'(lambda (expr)
`(,(incf key) ,expr))
exprs))))
所以我在这个函数上执行了macroexpand-1,我大致了解这个宏的工作原理,但我对格雷厄姆如何嵌套反引号`以及他如何使用 ,@ 来扩展案例感到非常困惑。
- 什么时候可以嵌套反引号?
- 为什么 Graham 在本例中要嵌套反引号?
- 为什么
,@将案例扩展为(random ,(length exprs))案例? - 我知道
mapcar主要是为了增加key,但是这个宏怎么知道应用mapcar一共(random ,(length exprs))次呢? - comma-at
,@正在拼接的隐式列表是如何形成的?
请注意,我很愚蠢,所以请用最基本的术语解释一下。
编辑:
我现在明白,最里面的反引号 (,(incf key) ,expr) 确保首先评估此函数,因此它大致相当于 (list (incf key) expr),然后
,@(let ((i 0))
(mapcar #'(lambda (expr)
`(,(incf i) ,expr))
args))
被评估成类似于列表'((0 a_0) (1 a_1) ... (n a_n)) 的东西,因为我们有,@,所以它被“拼接”到
((0 a_0))
((1 a_n))
.
.
.
((n a_n))
最后(case (random ,(length exprs)) 被评估为
case (random n) 它也给了我们外括号,给我们留下了
(case (random n)
((0 a_0))
((1 a_n))
.
.
.
((n a_n)))
我得到的事件顺序正确吗?我在网上找不到任何资源来验证,格雷厄姆的书也没有像这样分解它。
【问题讨论】:
-
((0 a_0))不是有效的case子句。括号太多了。 -
@RainerJoswig 当我阅读规范时,
((0 a_0))是一个有效的子句,它只是不符合人们的期望。具体来说,它与((0 a_0) (progn))相同,因此该子句的含义是“如果事物为0 或a_0,则评估为nil”。尽管如果所有子句都在一个 case 中并且采用这种形式,那么 case 不会真正做任何事情。 -
@DanRobertson:在我们正在讨论的 RANDOM-CHOICE 案例中,它不会做任何有用的事情,请参阅上面的示例 - 这意味着它不是用于此处目的的有效子句。一般来说,你是对的 - CASE 子句根据 CL 语法是有效的。
标签: lisp common-lisp