我的目标是拥有一个宏,该宏可以自动构建函数,并带有在其他位置生成的参数列表。我希望宏返回一个由函数及其使用的参数列表(符号列表)组成的列表。我正在使用SBCL。
假设参数列表由生成:
(defun input-syms ()
(list 'in1 'in2 'in3))
;;=> (IN1 IN2 IN3)
在Nested `defun` produces a repeated warning in Allegro Common Lisp中给出有用的答案之后,我像这样使用labels
(仅出于示例目的,添加列表的元素):
(defmacro create-funtest ()
(let ((input-list (input-syms)))
`(labels ((fun-created ,input-list
(reduce #'+ (list ,@input-list))))
#'fun-created)))
(funcall (create-funtest) 2 2 3) ;=> 7
这似乎可行,尽管我认为可以有一种更简单的方法。 (list ,@input-list)
似乎是不必要的,但仅用,input-list
替换它是行不通的。
这是我不知所措的地方,它似乎与,input-list
的确切含义有关。我认为这与以下事实有关:我们正在操纵符号,因此我尝试在其中插入symbol-value
,但无济于事。
用不起作用代码表示的我想要获得的是:
(defmacro create-funtest2 ()
(let ((input-list (input-syms)))
`(labels ((fun-created ,input-list
(reduce #'+ (list ,@input-list))))
(list #'fun-created ,input-list))))
必须返回:(#<FUNCTION ...> (IN1 IN2 IN3))
。
但是调用create-funtest2
会产生编译错误The variable IN2 is unbound.
。我认为它正在尝试评估符号,而不是按原样给我符号。
我需要能够获得用于构建函数的符号,我在随后调用函数时使用它们来知道哪个输入是什么。符号列表也可以通过create-funtest
宏进行修改,因此我真的需要从宏内部获取它。
感谢Rainer Joswig的回答。困扰我的事情实际上是将符号列表作为符号返回。我猜create-funtest2
的扩展代码(从您给出的扩展中)应该看起来像:
(LABELS ((FUN-CREATED (IN1 IN2 IN3)
(REDUCE #'+ (LIST IN1 IN2 IN3))))
(LIST #'FUN-CREATED (LIST 'IN1 'IN2 'IN3)))
因此宏的输出为(#<FUNCTION ...> (IN1 IN2 IN3))
。
问题是我想评估input-list
,但将其元素保留为符号(不确定对不起,我措辞不正确)。
谢谢coredump。 create-funtest2
的工作版本为:
(defmacro create-funtest2 ()
(let ((input-list (test-input-syms)))
`(labels ((fun-created ,input-list
(reduce #'+ (list ,@input-list))))
(list #'fun-created (quote ,input-list)))))
提供了哪些扩展名(感谢Rainer的代码段):
(let ((*print-circle* t)
(*PRINT-RIGHT-MARGIN* 50))
(pprint (copy-tree (macroexpand-1 '(create-funtest2)))))
=>
(LABELS ((FUN-CREATED (IN1 IN2 IN3)
(REDUCE #'+ (LIST IN1 IN2 IN3))))
(LIST #'FUN-CREATED '(IN1 IN2 IN3)))
并通过以下方式调用:
(defparameter *fun-created2* (create-funtest2))
(funcall (car *fun-created2*) 1 2 3) ; => 6, OK
(second *fun-created2*) ; => (IN1 IN2 IN3), OK
答案 0 :(得分:2)
输入列表为(IN1 IN2 IN3)
。
这有效:
(reduce #'+ (list IN1 IN2 IN3))
这不起作用:
(reduce #'+ (IN1 IN2 IN3))
原因:没有功能IN1
。
Macroexpand是您的朋友:
CL-USER 58 > (let ((*print-circle* t)
(*PRINT-RIGHT-MARGIN* 50))
(pprint (copy-tree (macroexpand-1 '(create-funtest2)))))
(LABELS ((FUN-CREATED (IN1 IN2 IN3)
(REDUCE #'+ (LIST IN1 IN2 IN3))))
(LIST #'FUN-CREATED (IN1 IN2 IN3)))
扩展后的代码有两个问题:
IN1
不存在IN2
和IN3
不存在也许macroexpand
将帮助您解决问题。
要获得更多帮助,您需要稍微更好地解释您想做什么。
答案 1 :(得分:2)
当给定任意长输入列表时,您可能要使用reduce
的原因是,函数调用受CALL-ARGUMENT-LIMIT
限制。但是,在这里,您将符号列表用作函数参数,该列表受LAMBDA-PARAMETERS-LIMIT
的限制。同样,第二个必须大于或等于第一个。因此,如果符号列表足够短,可以用作参数列表,那么它也足够短,可以用作+
调用中的参数。
(defun input-symbols ()
'(in1 in2 in3))
(defmacro create-funtest ()
(let ((args (input-symbols)))
`(lambda ,args (+ ,@args))))
在上面,我还使用了匿名函数,但这并不重要。
使用与上述相同的方法重写的第二个版本是:
(defmacro bad-create-funtest2 ()
(let ((args (input-symbols)))
`(list (lambda ,args (+ ,@args))
,args)))
macroexpand
对此有何评论?
(macroexpand '(bad-create-funtest2))
=> (LIST (LAMBDA (IN1 IN2 IN3) (+ IN1 IN2 IN3))
(IN1 IN2 IN3))
在这里,您可以看到您正在尝试使用参数in1
和in2
来调用in3
。
您不想评估符号列表,只需传递未评估的符号即可。
(defmacro create-funtest2 ()
(let ((args (input-symbols)))
`(list (lambda ,args (+ ,@args))
(quote ,args))))
通过引用该值,可以确保该值不会被评估。
(macroexpand '(create-funtest2))
=> (LIST (LAMBDA (IN1 IN2 IN3) (+ IN1 IN2 IN3))
'(IN1 IN2 IN3))