【问题标题】:Issues when write `loop ... collect` in macro在宏中编写 `loop ... collect` 时出现问题
【发布时间】:2016-08-19 22:34:19
【问题描述】:

伙计们。

今天我想编写 sigma 宏来计算灵活表达式输入的总和。

下面的代码是我今天下午写的。但它不符合我的目的。

(defmacro sigma (exp ll)
  `(+ ,@(loop for i in ll collect
             (progn (setf (elt exp 1) i)
                    (print exp)
                    exp)))
  )

>>(pprint (macroexpand-1 '(sigma (+ 1 2) (2 3 4))))
>>(+ 2 2) 
  (+ 3 2) 
  (+ 4 2) 
  (+ (+ 4 2) (+ 4 2) (+ 4 2))

我希望它可以工作 (+ (+ 2 2) (+ 3 2) (+ 4 2))loop collect 给我一个奇怪的答案。

为什么会这样?我有什么方法可以解决这个问题吗?

【问题讨论】:

    标签: macros lisp common-lisp clisp


    【解决方案1】:

    您正在改变文字数据(引用)。如果您同意在该循环期间,绑定到 exp 的列表 (+ 1 2) 在每次迭代中都是相同的,并且您在每次迭代中对第二个元素进行变异,那么很容易想象一个列表收集了相同的列表 exp 3 次将有 3 个完全相同的元素与第二个元素的最后一个突变。

    这绝不是宏的功能。对所有引用的数据进行变异可以产生这样的结果。该标准规定结果将是未定义的,因此没有实现者需要解决这个问题,并且您会从特定实现的其他方面获得意外行为。

    编译后的文件可能会将所有引用的数据合并在一起,从而使代码中的'(+ 1 2) 其他位置也可能受到此宏的影响。

    要解决这个问题,请不要变异:

    (defmacro sigma ((op r &rest rest) ll)
      `(+ ,@(loop :for i :in ll 
                  :collect (list* op i rest))))
    
    (macroexpand-1 '(sigma (+ 1 2) (2 3 4)))
    ; ==> (+ (+ 2 2) (+ 3 2) (+ 4 2))
    

    这样做的好处是可以保证模板中至少有两个参数。

    (macroexpand-1 '(sigma (x) (2 3 4)))
    ; ==> *** - SIGMA: (X) does not match lambda list element (OP R &REST REST)
    

    【讨论】:

    • 即使他没有对其进行变异,他每次通过循环都会收集相同的 exp 对象。
    • @Barmar 我记得我曾经NCONC-ed 两次相同的参数以获得双倍大小的列表:-)
    【解决方案2】:

    如果你想要一个新鲜的清单,那么copy-list 是一种方法:

    (defmacro sigma (exp ll)
      `(+ ,@(loop for i in ll and exp1 = (copy-list exp)
                  do (setf (second exp1) i)
                  collect exp1)))
    

    嵌套反引号表达式也是可能的:

    (defmacro sigma ((op arg0 &rest args) ll)
      (declare (ignore arg0))
      `(+ ,@(loop for i in ll collect `(,op ,i ,@args))))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-11
      • 1970-01-01
      • 1970-01-01
      • 2023-03-21
      相关资源
      最近更新 更多