【问题标题】:What is the best way of combining &key and &rest in a lisp macro's lambda list?在 lisp 宏 lambda 列表中组合 & key 和 &rest 的最佳方法是什么?
【发布时间】:2017-02-17 16:50:33
【问题描述】:

我使用宏实现了Heap's algorithm。它工作正常,但我想对其进行调整,以便它可以按需生成照应或非照应代码。换句话说,我想让宏要么制作它将置换的序列的内部副本,要么处理宏之外可用的序列。

我完全不满意,彻头彻尾的尴尬代码是:

;; Anaphoric version
;; To make it non-anaphoric, substitute (,var (copy-seq ,vec)) for (,var ,vec)
(defmacro run-permutations (var vec &rest body)
 "Executes body for all permutations of vec, which is stored in variable var"
  `(let ((,var ,vec))
     (labels ((generate (&optional (n (length ,var)))
       (if (= n 1)
         (progn ,@body)
        (progn 
           (loop for i from 0 below (1- n)
                 do (progn 
                      (generate (1- n))
                      (rotatef (aref ,var (if (evenp n) i 0))
                               (aref ,var (1- n)))))
           (generate (1- n))))))
      (generate))))

? (run-permutations v "123" (pprint v))
"123"
"213"
"312"
"132"
"231"
"321"
?

我想写一些像这样工作的东西......

? (setf v "123")
? (run-permutations :anaphoric t v "123" (...do stuff...))
? v
"321"

? (setf v "123")
? (run-permutations v "123" (...do stuff...))
? v
"123"

...但我还没有找到令人满意的 &rest&key 组合或任何其他编写 lambda 列表的方法。

所以我的问题是:有没有办法做到这一点,最好不用编写更多代码来解析宏的 lambda 列表?还是有另一种或多或少标准(并且可能更优雅)的解决方案?我强烈怀疑后者。

非常感谢您的意见。与往常一样,代码中的任何其他 cmets 也会受到赞赏。

更新

太棒了!我选择将gensym 用于n,因为body 是从递归内部调用的,我看不出如何从其他地方调用它——至少在不重写所有内容的情况下是这样。

我还添加了另一个功能和一个小的优化。如果您好奇,更新的版本是:

(defmacro do-permutations ((var vec &key anaphoric (len (length vec))) &body body)
  "Executes body for all permutations of vec, which is stored in variable var.
   KEYS:
     anaphoric: if defined, modifies var outside the macro, preserves it otherwise
     len: number of items that will be permuted, default is the full vector"
  (let ((n (gensym)))
  `(let ((,var ,(if anaphoric vec `(copy-seq ,vec))))
     (labels ((generate (&optional (,n ,len))
                (if (= ,n 1)
                    (progn ,@body)
                  (let ((n-1 (1- ,n)))
                    (loop for i from 0 below n-1
                          do (progn
                               (generate n-1)
                               (rotatef (aref ,var (if (evenp ,n) i 0))
                                        (aref ,var n-1))))
                    (generate n-1)))))
       (generate)))))

最后,我尝试在do 之后删除progn,但它不起作用,因为此时必须评估 2 个表达式。

【问题讨论】:

    标签: macros lisp common-lisp


    【解决方案1】:

    正确缩进代码:

    (defmacro run-permutations (var vec &rest body)
      "Executes body for all permutations of vec, which is stored in variable var"
      `(let ((,var ,vec))
         (labels ((generate (&optional (n (length ,var)))
                    (if (= n 1)
                        (progn ,@body)
                      (progn 
                        (loop for i from 0 below (1- n)
                              do (progn 
                                   (generate (1- n))
                                   (rotatef (aref ,var (if (evenp n) i 0))
                                            (aref ,var (1- n)))))
                        (generate (1- n))))))
           (generate))))
    

    使用类似的东西:

    (do-permutations (v "123" :anaphoric t)
      (some)
      (stuff))
    

    使用宏:

    (defmacro do-permutations ((var vec &key anaphoric) &body body)
      ...)
    

    其他名称:doing-permutationswith-permutations、...

    还要注意,主体可以用&body 声明,而不是&rest。语义是相同的,但人们期望它的缩进不同。 &body 表示下面是 Lisp 表单列表。

    loopdo 之后,您也不需要progn

    body 看到变量 n。你可能会想到身体的另一个地方......

    【讨论】:

    猜你喜欢
    • 2015-07-12
    • 2019-08-03
    • 1970-01-01
    • 2022-12-24
    • 1970-01-01
    • 2010-09-28
    • 1970-01-01
    • 2020-09-13
    • 1970-01-01
    相关资源
    最近更新 更多