如何将这个 Common Lisp 函数转换成宏?
How to convert this Common Lisp function into a macro?
我正在使用 SBCL、Slime 和 Emacs 在 Common Lisp 中进行开发。
我有这个功能:
(defun build-cond-action-pairs (&rest var)
(labels ((aux (xs-left accu)
(cond ((null (cddr xs-left))
(append accu (list (list (first xs-left)
(second xs-left)))))
(t (aux (cddr xs-left)
(append accu (list (list (first xs-left)
(second xs-left)))))))))
(aux var nil)))
我还定义了这两个变量:
CL-USER>(defparameter var-a 1)
VAR-A
CL-USER> (defparameter var-b 1)
VAR-B
当我调用函数时:
CL-USER> (build-cond-action-pairs "fish are cool" (incf var-a) "amphibians are cool" (incf var-b))
正如预期的那样,对参数求值:
(("fish are cool" 2) ("amphibians are cool" 2))
我想把这个函数变成一个宏。因此,不会评估参数。
期望的输出结果将是:
(("fish are cool" (incf var-a)) ("amphibians are cool" (incf var-b)))
我试过:
CL-USER> (defmacro macro-build-cond-action-pairs (&rest var)
`(labels ((aux (,xs-left ,accu)
(cond ((null (cddr ,xs-left))
(append ,accu (list (list (first ,xs-left)
(second ,xs-left)))))
(t (aux (cddr ,xs-left)
(append ,accu (list (list (first ,xs-left)
(second ,xs-left)))))))))
(aux ,var nil)))
但是,它不起作用:
; in: DEFMACRO MACRO-BUILD-COND-ACTION-PAIRS
; `(LABELS ((AUX (,XS-LEFT ,ACCU)
; (COND (# #) (T #))))
; (AUX ,VAR NIL))
; --> SB-IMPL::|List| SB-IMPL::|List| SB-IMPL::|List|
; ==>
; (SB-IMPL::|List| XS-LEFT ACCU)
;
; caught WARNING:
; undefined variable: COMMON-LISP-USER::ACCU
;
; caught WARNING:
; undefined variable: COMMON-LISP-USER::XS-LEFT
;
; compilation unit finished
; Undefined variables:
; ACCU XS-LEFT
; caught 2 WARNING conditions
MACRO-BUILD-COND-ACTION-PAIRS
感觉像是一个包(或命名空间问题)。也许根是标签部分。不知道怎么解决。
我该如何解决这个问题?
谢谢。
;; from:
;; (build-cond-action-pairs "fish are cool" (incf var-a)
;; "amphibians are cool" (incf var-b))
;; the macro-expansion should be:
;; (("fish are cool" (incf var-a)) ("amphibians are cool" (incf var-b)))
;; of course, this is makeable - with common lisp.
但是,随后会执行宏 - 但无法执行此操作
因为此列表的第一个位置 ("fish are cool" (incf var-a))
不是 return 函数。
但是如果macro-expansion是
;; (list '("fish are cool" (incf var-a)) '("amphibians are cool" (incf var-b)))
;; which is equivalent to:
;; (list (quote ("fish are cool" (incf var-a))) (quote ("amphibians are cool" (incf var-b))))
;; it would evaluate to:
;; (("fish are cool" (incf var-a)) ("amphibians are cool" (incf var-b)))
这将是普通的 lisp。
虽然它没有扩展为有效的 lisp 表达式,但我们可以实现它,即使它真的扩展为:
(("fish are cool" (incf var-a)) ("amphibians are cool" (incf var-b)))
因为使用 macroexpand-1
你可以检查宏扩展到什么 - 无论扩展表达式的计算是否会给出错误。所以我会告诉你这两种可能性。
;; we want
;; (macroexpand-1 '(build-cond-action-pairs "fish are cool" (incf var-a)
;; "amphibians are cool" (incf var-b)))
;; returns:
;; (("fish are cool" (incf var-a)) ("amphibians are cool" (incf var-b)))
(defmacro build-cond-action-pairs (&rest var)
...)
;; so we want this macro loops over its var elements and pairs the elements.
;; we need a function which takes a list and generates a list of lists
;; where the inner lists group pairs of elements.
;; One can achieve this with loop.
(defun to-pairs (l)
"Group elements of list l in lists of length 2 - pairs."
(loop for (a b &rest x) on l by #'cddr
collect (list a b)))
;; a macro takes its arguments list and doesn't evaluate its arguments.
;; we can use inside macros such functions to re-arrange the arguments list
;; - we can use list/data manipulation functions to re-arrange code - this
;; is the power of macros in common lisp!
(defmacro build-cond-action-pairs (&rest var)
`,(to-pairs `,var))
;; try it out:
(macroexpand-1 '(build-cond-action-pairs "fish are cool" (incf var-a)
"amphibians are cool" (incf var-b)))
;; returning:
(("fish are cool" (INCF VAR-A)) ("amphibians are cool" (INCF VAR-B))) ;
T
所以这完全可以扩展到您想要的方式。
但是执行宏的时候会报错:
(build-cond-action-pairs "fish are cool" (incf var-a)
"amphibians are cool" (incf var-b))
*** - EVAL: ("fish are cool" (INCF VAR-A)) is not a function name; try using a
symbol instead
The following restarts are available:
USE-VALUE :R1 Input a value to be used instead.
ABORT :R2 Abort main loop
(我在 common lisp 的实现 clisp
中尝试了它 - 只是因为我经常用它进行非常快速的测试 - 而对于严肃的编程我使用 emacs + sbcl)。
我只是想向您展示 lisp 甚至可以做到这一点。
所以让我们用 list
和 quote
构建另一个变体:
(defun to-quoted-pairs (l)
"Group elements of list l in quoted lists of length 2 - quoted pairs."
(loop for (a b &rest x) on l by #'cddr
collect (list 'quote (list a b))))
(defmacro build-cond-action-pairs (&rest var)
`,(to-quoted-pairs `,var))
(macroexpand-1 '(build-cond-action-pairs "fish are cool" (incf var-a)
"amphibians are cool" (incf var-b)))
;;=> ('("fish are cool" (INCF VAR-A)) '("amphibians are cool" (INCF VAR-B))) ;;=> T
这几乎就是我们想要的 - 我们只想在开始时 cons
一个 #'list
。
所以:
(defmacro build-cond-action-pairs (&rest var)
(cons 'list `,(to-quoted-pairs `,var)))
(macroexpand-1 '(build-cond-action-pairs "fish are cool" (incf var-a)
"amphibians are cool" (incf var-b)))
;;=> (LIST '("fish are cool" (INCF VAR-A))
;;=> '("amphibians are cool" (INCF VAR-B))) ;
;;=> T
;; That's it! and we can run it without error:
(build-cond-action-pairs "fish are cool" (incf var-a)
"amphibians are cool" (incf var-b))
;;=> (("fish are cool" (INCF VAR-A)) ("amphibians are cool" (INCF VAR-B)))
瞧!我们成功了!
@Gwang-JinKim 给出了解决方案(感谢帮助!)。但是,他的解决方案改变了我原来的答案中描述的递归方法。
我最终找到了一种修复宏的方法,使其与原始问题非常相似。基本上,有必要删除一些逗号并在尾部调用之前插入一个 (quote ...)
。
查看:
CL-USER> (defmacro macro-build-cond-action-pairs (&rest var)
`(labels ((aux (xs-left accu)
(cond ((null (cddr xs-left))
(append accu (list (list (first xs-left)
(second xs-left)))))
(t (aux (cddr xs-left)
(append accu (list (list (first xs-left)
(second xs-left)))))))))
(aux (quote ,var) nil)))
有效:
CL-USER> (macro-build-cond-action-pairs "fish are cool" (incf var-a) "amphibians are cool" (incf var-b))
(("fish are cool" (INCF VAR-A)) ("amphibians are cool" (INCF VAR-B)))
我正在使用 SBCL、Slime 和 Emacs 在 Common Lisp 中进行开发。
我有这个功能:
(defun build-cond-action-pairs (&rest var)
(labels ((aux (xs-left accu)
(cond ((null (cddr xs-left))
(append accu (list (list (first xs-left)
(second xs-left)))))
(t (aux (cddr xs-left)
(append accu (list (list (first xs-left)
(second xs-left)))))))))
(aux var nil)))
我还定义了这两个变量:
CL-USER>(defparameter var-a 1)
VAR-A
CL-USER> (defparameter var-b 1)
VAR-B
当我调用函数时:
CL-USER> (build-cond-action-pairs "fish are cool" (incf var-a) "amphibians are cool" (incf var-b))
正如预期的那样,对参数求值:
(("fish are cool" 2) ("amphibians are cool" 2))
我想把这个函数变成一个宏。因此,不会评估参数。
期望的输出结果将是:
(("fish are cool" (incf var-a)) ("amphibians are cool" (incf var-b)))
我试过:
CL-USER> (defmacro macro-build-cond-action-pairs (&rest var)
`(labels ((aux (,xs-left ,accu)
(cond ((null (cddr ,xs-left))
(append ,accu (list (list (first ,xs-left)
(second ,xs-left)))))
(t (aux (cddr ,xs-left)
(append ,accu (list (list (first ,xs-left)
(second ,xs-left)))))))))
(aux ,var nil)))
但是,它不起作用:
; in: DEFMACRO MACRO-BUILD-COND-ACTION-PAIRS
; `(LABELS ((AUX (,XS-LEFT ,ACCU)
; (COND (# #) (T #))))
; (AUX ,VAR NIL))
; --> SB-IMPL::|List| SB-IMPL::|List| SB-IMPL::|List|
; ==>
; (SB-IMPL::|List| XS-LEFT ACCU)
;
; caught WARNING:
; undefined variable: COMMON-LISP-USER::ACCU
;
; caught WARNING:
; undefined variable: COMMON-LISP-USER::XS-LEFT
;
; compilation unit finished
; Undefined variables:
; ACCU XS-LEFT
; caught 2 WARNING conditions
MACRO-BUILD-COND-ACTION-PAIRS
感觉像是一个包(或命名空间问题)。也许根是标签部分。不知道怎么解决。
我该如何解决这个问题?
谢谢。
;; from:
;; (build-cond-action-pairs "fish are cool" (incf var-a)
;; "amphibians are cool" (incf var-b))
;; the macro-expansion should be:
;; (("fish are cool" (incf var-a)) ("amphibians are cool" (incf var-b)))
;; of course, this is makeable - with common lisp.
但是,随后会执行宏 - 但无法执行此操作
因为此列表的第一个位置 ("fish are cool" (incf var-a))
不是 return 函数。
但是如果macro-expansion是
;; (list '("fish are cool" (incf var-a)) '("amphibians are cool" (incf var-b)))
;; which is equivalent to:
;; (list (quote ("fish are cool" (incf var-a))) (quote ("amphibians are cool" (incf var-b))))
;; it would evaluate to:
;; (("fish are cool" (incf var-a)) ("amphibians are cool" (incf var-b)))
这将是普通的 lisp。
虽然它没有扩展为有效的 lisp 表达式,但我们可以实现它,即使它真的扩展为:
(("fish are cool" (incf var-a)) ("amphibians are cool" (incf var-b)))
因为使用 macroexpand-1
你可以检查宏扩展到什么 - 无论扩展表达式的计算是否会给出错误。所以我会告诉你这两种可能性。
;; we want
;; (macroexpand-1 '(build-cond-action-pairs "fish are cool" (incf var-a)
;; "amphibians are cool" (incf var-b)))
;; returns:
;; (("fish are cool" (incf var-a)) ("amphibians are cool" (incf var-b)))
(defmacro build-cond-action-pairs (&rest var)
...)
;; so we want this macro loops over its var elements and pairs the elements.
;; we need a function which takes a list and generates a list of lists
;; where the inner lists group pairs of elements.
;; One can achieve this with loop.
(defun to-pairs (l)
"Group elements of list l in lists of length 2 - pairs."
(loop for (a b &rest x) on l by #'cddr
collect (list a b)))
;; a macro takes its arguments list and doesn't evaluate its arguments.
;; we can use inside macros such functions to re-arrange the arguments list
;; - we can use list/data manipulation functions to re-arrange code - this
;; is the power of macros in common lisp!
(defmacro build-cond-action-pairs (&rest var)
`,(to-pairs `,var))
;; try it out:
(macroexpand-1 '(build-cond-action-pairs "fish are cool" (incf var-a)
"amphibians are cool" (incf var-b)))
;; returning:
(("fish are cool" (INCF VAR-A)) ("amphibians are cool" (INCF VAR-B))) ;
T
所以这完全可以扩展到您想要的方式。 但是执行宏的时候会报错:
(build-cond-action-pairs "fish are cool" (incf var-a)
"amphibians are cool" (incf var-b))
*** - EVAL: ("fish are cool" (INCF VAR-A)) is not a function name; try using a
symbol instead
The following restarts are available:
USE-VALUE :R1 Input a value to be used instead.
ABORT :R2 Abort main loop
(我在 common lisp 的实现 clisp
中尝试了它 - 只是因为我经常用它进行非常快速的测试 - 而对于严肃的编程我使用 emacs + sbcl)。
我只是想向您展示 lisp 甚至可以做到这一点。
所以让我们用 list
和 quote
构建另一个变体:
(defun to-quoted-pairs (l)
"Group elements of list l in quoted lists of length 2 - quoted pairs."
(loop for (a b &rest x) on l by #'cddr
collect (list 'quote (list a b))))
(defmacro build-cond-action-pairs (&rest var)
`,(to-quoted-pairs `,var))
(macroexpand-1 '(build-cond-action-pairs "fish are cool" (incf var-a)
"amphibians are cool" (incf var-b)))
;;=> ('("fish are cool" (INCF VAR-A)) '("amphibians are cool" (INCF VAR-B))) ;;=> T
这几乎就是我们想要的 - 我们只想在开始时 cons
一个 #'list
。
所以:
(defmacro build-cond-action-pairs (&rest var)
(cons 'list `,(to-quoted-pairs `,var)))
(macroexpand-1 '(build-cond-action-pairs "fish are cool" (incf var-a)
"amphibians are cool" (incf var-b)))
;;=> (LIST '("fish are cool" (INCF VAR-A))
;;=> '("amphibians are cool" (INCF VAR-B))) ;
;;=> T
;; That's it! and we can run it without error:
(build-cond-action-pairs "fish are cool" (incf var-a)
"amphibians are cool" (incf var-b))
;;=> (("fish are cool" (INCF VAR-A)) ("amphibians are cool" (INCF VAR-B)))
瞧!我们成功了!
@Gwang-JinKim 给出了解决方案(感谢帮助!)。但是,他的解决方案改变了我原来的答案中描述的递归方法。
我最终找到了一种修复宏的方法,使其与原始问题非常相似。基本上,有必要删除一些逗号并在尾部调用之前插入一个 (quote ...)
。
查看:
CL-USER> (defmacro macro-build-cond-action-pairs (&rest var)
`(labels ((aux (xs-left accu)
(cond ((null (cddr xs-left))
(append accu (list (list (first xs-left)
(second xs-left)))))
(t (aux (cddr xs-left)
(append accu (list (list (first xs-left)
(second xs-left)))))))))
(aux (quote ,var) nil)))
有效:
CL-USER> (macro-build-cond-action-pairs "fish are cool" (incf var-a) "amphibians are cool" (incf var-b))
(("fish are cool" (INCF VAR-A)) ("amphibians are cool" (INCF VAR-B)))