gpt4 book ai didi

macros - 使用宏构建具有动态输入的函数

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

我的目标是拥有一个宏,可以使用其他地方生成的参数列表自动构建函数。我希望宏返回一个由函数及其使用的参数列表(符号列表)组成的列表。我正在使用 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 进行修改宏,所以我真的需要从宏内部获取它。

编辑 1

感谢 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 ,但将其元素保留为符号(抱歉,我不确定我的措辞是否正确)。

编辑2

谢谢你的核心转储。 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

最佳答案

简单版本

当给定任意长的输入列表时,您可能想要使用 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))

在这里,您可以看到您正在尝试使用参数 in2in3 调用 in1。您不想评估符号列表,只需将其不评估即可传递。

返回未计算的符号

(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))

关于macros - 使用宏构建具有动态输入的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50965761/

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