gpt4 book ai didi

macros - 在 Common Lisp 中正确地将列表传递给宏

转载 作者:行者123 更新时间:2023-12-02 10:02:59 25 4
gpt4 key购买 nike

我编写了一个宏来执行多个嵌套循环。我知道还有其他工具可以做到这一点,但我正在尝试学习如何编写宏,这似乎是一个很好的用例。它也有效(有点):

(defmacro dotimes-nested (nlist fn)
(let ((index-symbs nil)
(nlist (second nlist))) ;remove quote from the beginning of nlist
(labels
((rec (nlist)
(cond ((null nlist) nil)
((null (cdr nlist))
(let ((g (gensym)))
(push g index-symbs)
`(dotimes (,g ,(car nlist) ,g)
(funcall ,fn ,@(reverse index-symbs)))))
(t (let ((h (gensym)))
(push h index-symbs)
`(dotimes (,h ,(car nlist) ,h)
,(rec (cdr nlist))))))))
(rec nlist))))

运行:

(macroexpand-1 '(dotimes-nested '(2 3 5)
#'(lambda (x y z) (format t "~A, ~A, ~A~%" x y z))))

输出:

(DOTIMES (#:G731 2 #:G731)
(DOTIMES (#:G732 3 #:G732)
(DOTIMES (#:G733 5 #:G733)
(FUNCALL #'(LAMBDA (X Y Z) (FORMAT T "~A, ~A, ~A~%" X Y Z)) #:G731 #:G732
#:G733))))

并像这样调用宏:

(dotimes-nested '(2 3 5)
#'(lambda (x y z) (format t "~A, ~A, ~A~%" x y z)))

正确返回我所期望的:

0, 0, 0
0, 0, 1
0, 0, 2
0, 0, 3
0, 0, 4
0, 1, 0
0, 1, 1
0, 1, 2
0, 1, 3
0, 1, 4
0, 2, 0
0, 2, 1
0, 2, 2
0, 2, 3
0, 2, 4
1, 0, 0
1, 0, 1
1, 0, 2
1, 0, 3
1, 0, 4
1, 1, 0
1, 1, 1
1, 1, 2
1, 1, 3
1, 1, 4
1, 2, 0
1, 2, 1
1, 2, 2
1, 2, 3
1, 2, 4
2

但是,如果我这样调用它:

(let ((dims '(3 4)))
(dotimes-nested dims
#'(lambda (x y) (format t "~A, ~A~%" x y))))

我收到错误:

; in: LET ((DIMS '(3 4)))
; (DOTIMES-NESTED DIMS #'(LAMBDA (X Y) (FORMAT T "~A, ~A~%" X Y)))
;
; caught ERROR:
; during macroexpansion of (DOTIMES-NESTED DIMS #'(LAMBDA # #)). Use
; *BREAK-ON-SIGNALS* to intercept.
;
; The value DIMS is not of type LIST.

; (LET ((DIMS '(3 4)))
; (DOTIMES-NESTED DIMS #'(LAMBDA (X Y) (FORMAT T "~A, ~A~%" X Y))))
;
; caught STYLE-WARNING:
; The variable DIMS is defined but never used.
;
; compilation unit finished
; caught 1 ERROR condition
; caught 1 STYLE-WARNING condition

如何传入一个不被解释为符号而是被解释为值的变量?我应该使用 , 在宏中评估它吗?但我不知道怎么做。另外,这两种情况如何工作,a:使用文字 '(3 4) 调用宏,b:传入绑定(bind)到列表 '(3 4) 的符号?我需要为每个单独的宏吗?

最后,我还有一个次要问题。当我传入 nlist 的文字时,我必须使用 (second nlist) 来获取列表值。我理解这是因为 '(3 4) 在发送到宏之前被扩展为 (quote (3 4)) 。这是一个正确的假设吗?如果是这样,这是一般做法吗,即 - 使用传递给宏的文字列表的第二个值?

最佳答案

宏在代码编译或运行之前展开。然而,变量DIMS仅在代码运行时存在。赋予宏的参数是文字数据,因此当您执行

(dotimes-nested dims ...)

DIMS 不是对变量的引用(尚不存在),而只是一个符号。

调用宏时也无需引用列表,因为无论如何它都是文字数据。因此,您应该像 (dotimes-nested (2 3 4) ...) 那样调用它(并且不需要删除宏中的任何内容)。

如果您确实需要能够使用(运行时)维度变量,则应使用常规函数而不是宏。像这样的东西:

(defun dotimes-nested (nlist function)
(labels ((rec (nlist args)
(if (endp nlist)
(apply function (reverse args))
(destructuring-bind (first . rest) nlist
(dotimes (i first)
(rec rest (cons i args)))))))
(rec nlist nil)))

CL-USER> (let ((dims '(3 4)))
(dotimes-nested dims
#'(lambda (x y) (format t "~A, ~A~%" x y))))
0, 0
0, 1
0, 2
0, 3
1, 0
1, 1
1, 2
1, 3
2, 0
2, 1
2, 2
2, 3
NIL

关于macros - 在 Common Lisp 中正确地将列表传递给宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39528454/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com