【发布时间】:2019-03-06 22:32:35
【问题描述】:
我正在尝试将函数条件的符号传递给宏,并查看结果:
(defmacro macro-test-1 (form condition)
`(handler-case (funcall ,form)
(,condition (c)
(declare (ignore c))
(format t "~a" 'why?))))
(macro-test-1 #'(lambda () (error 'simple-type-error)) division-by-zero)
;; OK, I get the simple-type-error as expected.
(defun test-1 (condition)
(macro-test-1 #'(lambda () (error 'simple-type-error)) condition))
; in: DEFUN TEST-1
; (SB-INT:NAMED-LAMBDA TEST-1
; (CONDITION)
; (BLOCK TEST-1
; (MACRO-TEST-1 #'(LAMBDA () (ERROR 'SIMPLE-TYPE-ERROR)) CONDITION)))
;
; caught STYLE-WARNING:
; The variable CONDITION is defined but never used.
;
; compilation unit finished
; caught 1 STYLE-WARNING condition
TEST-1
;; what happened?
(test-1 'division-by-zero)
WHY?
NIL
;; what happened?
我对发生的事情感到很困惑,我一直在试图弄清楚它,我希望我错过了一些愚蠢的东西。
向上 1
和我想象的一样,愚蠢的错误,现在我意识到我在尝试做什么,宏会在编译时扩展,而我在运行时传递给函数的参数,所以宏不会收到条件论据正确。所以我看到了解决这个问题的两种可能性,将macro-test-1 转换为函数或将test-1 转换为宏。
其实我这里测试过,改成功能还是不行:
CL-USER> (defun macro-test-1 (form condition)
(handler-case (funcall form)
(condition (c)
(declare (ignore c))
(format t "~a" 'why?))))
; in: DEFUN MACRO-TEST-1
; (SB-INT:NAMED-LAMBDA MACRO-TEST-1
; (FORM CONDITION)
; (BLOCK MACRO-TEST-1
; (HANDLER-CASE (FUNCALL FORM)
; (CONDITION (C) (DECLARE #) (FORMAT T "~a" 'WHY?)))))
;
; caught STYLE-WARNING:
; The variable CONDITION is defined but never used.
;
; compilation unit finished
; caught 1 STYLE-WARNING condition
WARNING: redefining COMMON-LISP-USER::MACRO-TEST-1 in DEFUN
CL-USER> (macro-test-1 #'(lambda () (error 'simple-type-error)) 'division-by-zero)
WHY?
NIL
但是,当您将 macro-test-1 重新定义为宏,并将 test-1 重新定义为宏时:
CL-USER> (defmacro test-1 (condition)
`(macro-test-1 #'(lambda () (error 'simple-type-error)) ,condition))
TEST-1
CL-USER> (test-1 division-by-zero)
; Evaluation aborted on #<SIMPLE-TYPE-ERROR {1001BB8FF3}>.
我仍然不确定为什么函数不起作用,评估规则不是评估所有参数,然后将评估的参数传递给函数体?因为它不起作用?
向上 2
我知道handler-case 不起作用,因为您需要在编译时知道错误,并且将condition 作为运行时函数参数传递将无法知道编译时错误,所以它不工作。我强调这个单一的原因,而不是因为宏的发生具有编译时间,我在下面提到的一个问题导致我陷入了这整个混乱,并让我相信可以通过函数传递condition。我可以这样做:
(defmacro macro-test-1 (fn value)
`(funcall ,fn ,value 1))
(macro-test-1 #'= 1)
;; => T it is OK
(defun test-1 (fn value)
(macro-test-1 fn value))
(test-1 #'= 1)
;; => why it is OK?
上面的代码有效,即使我在运行时将参数传递给函数,为什么它有效?如果宏在编译时展开,为什么当我调用test-1 时它会工作?还是宏在编译时并不总是展开?我在这里错过了什么?
向上 3
我决定更深入,并尝试:
(defmacro macro-test-1 (fn value)
`(,fn ,value 1))
(macro-test-1 = 1)
;; => T it is OK
(defun test-1 (fn value)
(macro-test-1 fn value))
; in: DEFUN TEST-1
; (SB-INT:NAMED-LAMBDA TEST-1
; (FN VALUE)
; (BLOCK TEST-1 (MACRO-TEST-1 FN VALUE)))
;
; caught STYLE-WARNING:
; The variable FN is defined but never used.
; in: DEFUN TEST-1
; (MACRO-TEST-1 FN VALUE)
; ==>
; (FN VALUE 1)
;
; caught STYLE-WARNING:
; undefined function: FN
;
; compilation unit finished
; Undefined function:
; FN
; caught 2 STYLE-WARNING conditions
WARNING: redefining COMMON-LISP-USER::TEST-1 in DEFUN
TEST-1
是的,我知道如果我尝试如下所示,它不会按预期退出:
(test-1 '= 1)
; Evaluation aborted on #<UNDEFINED-FUNCTION FN {1004575323}>. ;
但我想找到一种方法让它工作,所以我尝试了,直到我可以,将macro-test-1重置为:
(defmacro macro-test-1 (fn value)
`(eval (,fn ,value 1)))
WARNING: redefining COMMON-LISP-USER::MACRO-TEST-1 in DEFMACRO
MACRO-TEST-1
(defun test-1 (fn value)
(macro-test-1 fn value))
WARNING: redefining COMMON-LISP-USER::TEST-1 in DEFUN
TEST-1
(test-1 '= 1)
T
当然这只适用于handler-case或case,如果我重新定义了它的宏,我认为这不应该是一个好的做法,我也不需要它,但我喜欢去它不需要的地方,好吧,那么,我学会了犯错。
【问题讨论】:
标签: macros common-lisp