如何在普通Lisp中将变量传递给宏?

时间:2018-11-18 17:05:48

标签: common-lisp

对于一个程序,我试图使用这样的宏来简化我的下游代码,并避免一次又一次地重复相同的代码:

public static int[] sortArray(int[] array) {

    int length = array.length;

    for (int i = 0; i < length - 1; i++) {
        int k = -1; // The index of last odd element
        for (int j = 0; j < length - i; j++)
            if (array[j] % 2 != 0) {
                if (k >= 0 && array[k] > array[j]) {
                    int temp = array[k];
                    array[k] = array[j];
                    array[j] = temp;
                }
                k = j;
            }
    }

    return array;
}

应该使用这样的列表:

(defmacro destructure (values &body body)
  `(let* ((other-values (rest values))
      (age (getf other-values :age))
      (len (getf other-values :len))
      (all (getf other-values :all)))
     (progn ,@(loop for e in body collect `(,@e)))))

想法是我应该能够运行以下代码:

'(name :age 1 :len 2 :all '(1 2 3 4 5))

或者使用这样的变量:

(destructure '(name :age 1 :len 2 :all '(1 2 3 4 5))
    (type-of age)
    (first all))

不必每次都访问不同的元素。当然,这是一个简化的示例,但是我要处理的清单是现实要长得多。

我发现很难做到这一点。基本上,上面的代码不起作用(除非我通过设置全局变量“ values”作弊来保存我的列表,以作弊),因为宏在不扩展其应指向的列表的情况下,正以同样的方式对待符号“ values”。 / p>

另一方面,我不能使用常规函数,因为这样我传递给主体的指令会立即执行,但我必须将它们放在let中。

我对这门语言还很陌生,我相信我可能会缺少一些东西,并且可能有一种方法可以实现它。有吗?

2 个答案:

答案 0 :(得分:4)

首先,对于您的问题,可以使用destructuring-bind

(destructuring-bind (name &key age len all)
    '(name :age 1 :len 2 :all (1 2 3 4 5))
  (list (type-of age)
        (type-of len)
        (type-of name)
        (first all)))
⇒ (BIT (INTEGER 0 4611686018427387903) SYMBOL 1)

现在,关于您的代码的一些注意事项:

您已将对values的引用放在反引号内,因此它不是对宏参数的引用,而是对扩展名为values的外部变量的扩展形式的自由引用。您可能打算做什么:

(defmacro destructure (values &body body)
  `(let* ((other-values ,(rest values)))
          (age (getf other-values :age))
          (len (getf other-values :len))
          (all (getf other-values :all)))
     …))

这有一个问题,就是它会从宏调用表单的外部遮盖任何名为other-values的变量。您应该使用gensyms来避免这种情况:

(defmacro destructure (values &body body)
  (let ((other-values (gensym "other-values")))
    `(let* ((,other-values ,(rest values)))
            (age (getf other-values :age))
            (len (getf other-values :len))
            (all (getf other-values :all)))
       …))

关于代码模板的注释:

(progn ,@(loop for e in body collect `(,@e))

简化为

(progn ,@(loop for e in body collect e))

简化为

(progn ,@body)

,由于let的主体已经是隐式的progn

,@body

最后,您的示例数据:

(name :age 1 :len 2 :all '(1 2 3 4 5))

实际上是:

(name :age 1 :len 2 :all (quote (1 2 3 4 5)))

阅读器始终将'扩展为quote形式。当您嵌套quote表单时,几乎总是在做错事。

答案 1 :(得分:3)

您需要评估宏中的values变量:

? (defmacro destructure (values &body body)
    `(let* ((other-values (rest ,values))
            (age (getf other-values :age))
            (len (getf other-values :len))
            (all (getf other-values :all)))
       (progn ,@body)))
DESTRUCTURE
? (destructure '(name :age 1 :len 2 :all '(1 2 3 4 5))
    (print (type-of age))
    (print (first all)))
;Compiler warnings :
;   In an anonymous lambda form at position 0: Unused lexical variable LEN

BIT 
QUOTE 
QUOTE

数据中的QUOTE不是您想要的。带引号的数据不需要内部引号。

? (destructure '(name :age 1 :len 2 :all (1 2 3 4 5))
    (print (type-of age))
    (print (first all)))
;Compiler warnings :
;   In an anonymous lambda form at position 0: Unused lexical variable LEN

BIT 
1 
1

它也适用于变量:

? (let ((values '(name :age 1 :len 2 :all (1 2 3 4 5))))
    (destructure values
      (print (type-of age))
      (print (first all))))

;Compiler warnings :
;   In an anonymous lambda form at position 59: Unused lexical variable LEN

BIT 
1 
1
? 

您还可以将三个变量声明为ignorable。这将使上面的警告消失。

要解决什么?

  • 变量other-values在正文中可见。