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

Macros 使用宏构建具有动态输入的函数,macros,arguments,common-lisp,Macros,Arguments,Common Lisp,我的目标是拥有一个宏,该宏使用在别处生成的参数列表自动构建函数。我希望宏返回一个由函数和它使用的参数列表(符号列表)组成的列表。我用的是SBCL 生成参数列表 假设参数列表由以下内容生成: (defun input-syms () (list 'in1 'in2 'in3)) ;;=> (IN1 IN2 IN3) 使用此参数列表构建函数 根据中的有用答案,我使用了类似的标签(添加列表元素,只是为了示例): 这似乎有效,尽管我认为可能有一种更简单的方法(list,@input list

我的目标是拥有一个宏,该宏使用在别处生成的参数列表自动构建函数。我希望宏返回一个由函数和它使用的参数列表(符号列表)组成的列表。我用的是SBCL

生成参数列表 假设参数列表由以下内容生成:

(defun input-syms ()
  (list 'in1 'in2 'in3))
;;=> (IN1 IN2 IN3)
使用此参数列表构建函数 根据中的有用答案,我使用了类似的
标签(添加列表元素,只是为了示例):

这似乎有效,尽管我认为可能有一种更简单的方法
(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))))
它必须返回:
(#(IN1 IN2 IN3))
。 但是调用
create-funtest2
会产生编译错误
变量IN2未绑定。
。我想它是在评估这个符号,而不是给我这个符号

我需要能够获得用于构建函数的符号,我在调用函数时使用它们来知道哪个输入是什么。这个符号列表也可以被
create funtest
宏修改,所以我真的需要从宏内部获取它

编辑1 谢谢雷纳·乔斯维格的回答。困扰我的事情实际上是将符号列表作为符号返回。我猜
create-funtest2
的扩展代码(来自您给出的扩展)应该如下所示:

(LABELS ((FUN-CREATED (IN1 IN2 IN3)
          (REDUCE #'+ (LIST IN1 IN2 IN3))))
  (LIST #'FUN-CREATED (LIST 'IN1 'IN2 'IN3)))
因此宏的输出是
(#(IN1 IN2 IN3))
。 问题是,我想计算
输入列表
,但将其元素保留为符号(不确定是否正确,抱歉)

编辑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提供的代码片段):

并按以下方式调用:

(defparameter *fun-created2* (create-funtest2))
(funcall (car *fun-created2*) 1 2 3) ; => 6, OK
(second *fun-created2*) ; => (IN1 IN2 IN3), OK
输入列表是
(IN1 IN2 IN3)

这项工作:

(reduce #'+ (list IN1 IN2 IN3))
这不起作用:

(reduce #'+ (IN1 IN2 IN3))
原因:1中没有函数

我是你的朋友

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
    不存在
  • 也许
    宏扩展将帮助您解决问题

    要获得更多帮助,您需要稍微更好地解释您想做什么。

    更简单的版本 当给定任意长的输入列表时,您可能希望使用
    reduce
    的原因是函数调用受到限制。但是,在这里,您将使用符号列表作为函数参数,这受到限制。此外,第二个必须大于或等于第一个。因此,如果符号列表足够短,可以用作参数列表,那么它也足够短,可以用作
    +
    调用中的参数

    (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 '(bad-create-funtest2))
    => (LIST (LAMBDA (IN1 IN2 IN3) (+ IN1 IN2 IN3))
             (IN1 IN2 IN3))
    
    在这里,您可以看到您正试图使用参数
    in2
    in3
    调用
    in1
    。 您不想对符号列表求值,只需传递未求值的符号即可

    (macroexpand '(create-funtest2))
    => (LIST (LAMBDA (IN1 IN2 IN3) (+ IN1 IN2 IN3))
             '(IN1 IN2 IN3))
    
    返回未计算的符号 通过引用该值,可以确保不会计算该值

    (macroexpand '(create-funtest2))
    => (LIST (LAMBDA (IN1 IN2 IN3) (+ IN1 IN2 IN3))
             '(IN1 IN2 IN3))
    

    就这样!关于
    减少
    的观点,这只是为了举例。有点傻,但我所需要的只是(引用,args)。我将使用工作版本的
    create-funtest2
    编辑我的问题。我发现“(list,@input list)似乎没有必要,但用just代替它,input list不起作用”。我在深水区发现一名潜水同伴。:)我清楚地记得这条学习曲线,所以很好地掌握正在发生的事情是很好的,因为编写宏所需的一项基本技能是在编写前一个代码的同时,保持扩展代码与其扩展之间的差异。简言之,仅使用“(reduce'+,inputs)”就可以“生成”(reduce'+(in1in2in3)),完全不是您想要的。Meta tip:macroexpand-1Indeed,我通过查看扩展了解到了这一点。我的一些最大的惊喜来自于简单地在扩展代码中打印出值(因此我在调用macroexpansion时可以看到它们的输出)。
    (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))