(defmacro随机选择(& rest exprs) (案例(随机,(长度表达)) ,@(let((key -1)) (mapcar#'(lambda(expr) (,(incf key),expr)) …
(案例(随机,(长度表达)) ,@(let((key -1)) (mapcar#'(lambda(expr)
什么时候可以嵌套反引号?
你可以随时嵌套反引号。但是请注意,此代码不会嵌套它们:
`(foo ; in 1 backquote here ,@(cons 'bar ; in 0 backquotes here `(baz ; in 1 backquotes here ,(random 3))))
嵌套反引号看起来像:
`(let ((x `(,,y ,z))) ...)
为什么Graham在这个例子中嵌套了反引号?
他没有筑巢。他用第一个反引号生成案例的外部主体,然后用生成的案例填充它 mapcar 。要编写为每个案例生成的代码,他使用第二个反引号
mapcar
为什么 ,@ 将案件扩展到 (random ,(length exprs)) 案件?
,@
(random ,(length exprs))
它没有这样做。它扩展到 (length exprs) 案例。严格地说,它合并在由其中的任何内容返回的事物列表中,在这种情况下是表达式列表。
(length exprs)
我知道mapcar主要是为了我们可以增加密钥,但是这个宏如何知道总共应用mapcar (random ,(length exprs)) 次?
那不是什么了 mapcar 是为了什么或用于什么。
逗号,@是拼接形成的隐式列表怎么样?
这就是mapcar所做的。
为了解决你的编辑问题,你有一些事情是正确的,但你有太多的问题。
mapcar 按顺序将函数应用于列表的每个元素,将结果收集到列表中:
CL-USER> (mapcar #'1+ '(1 2 3)) (2 3 4) CL-USER>(let ((key -1)) (mapcar (lambda (x) `(,incf key) ,x)) '(foo bar (baz wazoo))) ((0 FOO) (1 BAR) (2 (BAZ WAZOO)))
这将进入体内 case 表达式:如果随机值为0则返回 FOO ,如果它是1那么 BAR , 等等。
case
FOO
BAR
为了获得这个随机值,你可以做到 (random 3) 对于0到2之间的随机整数。
(random 3)
写它的另一种方法(摆脱LET,MAPCAR +副作用INCF代码):
CL-USER 44 > (defmacro random-choice (&rest exprs &aux (n (length exprs))) `(case (random ,n) ,@(loop for ci below n and expr in exprs collect `(,ci ,expr)))) RANDOM-CHOICE CL-USER 45 > (macroexpand-1 '(random-choice 10 21 32 43)) (CASE (RANDOM 4) (0 10) (1 21) (2 32) (3 43))
宏使用带有计算的反引号形式。我们可以提取计算并将部分分配给变量:
CL-USER 46 > (defmacro random-choice (&rest exprs &aux (n (length exprs))) (let ((keyform `(random ,n)) (clauses (loop for ci below n and expr in exprs collect `(,ci ,expr)))) `(case ,keyform ,@clauses))) RANDOM-CHOICE CL-USER 47 > (macroexpand-1 '(random-choice 10 21 32 43)) (CASE (RANDOM 4) (0 10) (1 21) (2 32) (3 43))
正如您所看到的那样,反引号形式可以独立计算,并在最后以反引号形式组装。
当宏函数包含代码片段时,最好将它们保留为引用或反引号形式 - >这使它们更容易在宏功能中识别。用列表计算替换它们(使用 list , cons ,...)不太方便,可读性差。但是,然后需要将反引号/不引号的顺序正确。在我的例子中,它稍微容易一些,因为部件是独立计算的。这有助于理解宏,因为它更符合语法 case :
list
cons
CASE keyform {normal-clause}* [otherwise-clause] normal-clause::= (keys form*)
这里我们只使用 keyform 来自的0..n-1条款 {normal-clause}* 。我们也不使用 otherwise-clause 。
keyform
{normal-clause}*
otherwise-clause