我想使用布尔逻辑和宏编写自己的if
。我想出了以下实现:
(defmacro -if (condition if-true if-false)
"Implements `if` in terms of Boolean logic only"
`(or (and ,condition ,if-true)
(and (not ,condition) ,if-false)))
我在几个案例中手动测试了它,它按预期工作。但后来我写了一个简单的测试函数来执行一系列测试并得到一个我仍然无法理解的结果。我写了这样的函数:
(defun -if-test ()
(let ((passed 0)
(test-format "TEST: ~50a ==> ~:[FAILURE~;SUCCESS~]~%")
(cases '((return-value (> 2 1) true)
(return-value (< 2 1) false)
(standard-output (> 2 1) "true")
(standard-output (< 2 1) "false"))))
(dolist (test-case cases)
(destructuring-bind (type test expected) test-case
(let ((result (case type
(return-value
(eq (-if test 'true 'false) expected))
(standard-output
(with-output-to-string (out)
(string= (-if test (print "true" out) (print "false" out)) expected)))
(otherwise (error "Unknown test type: ~a" type)))))
(when result (incf passed))
(format t test-format test-case result))))
(format t "Result: ~a/~a tests passed.~%" passed (length cases))))
当我运行测试时,我得到以下输出:
TEST: (RETURN-VALUE (> 2 1) TRUE) ==> SUCCESS
TEST: (RETURN-VALUE (< 2 1) FALSE) ==> FAILURE
TEST: (STANDARD-OUTPUT (> 2 1) true) ==> SUCCESS
TEST: (STANDARD-OUTPUT (< 2 1) false) ==> SUCCESS
Result: 3/4 tests passed.
NIL
第二个失败案例显然在手动操作时提供的结果与在此功能中运行时的结果不同。我尝试用SLDB调试它,结果确实与独立执行不同。我怀疑我错过了一些关键的执行细节或类似的东西。有人能解释一下这里发生了什么吗?帮助真的很感激。
P.S。我的实现是Clozure CL。
答案 0 :(得分:2)
您的测试用例是数据。您向-if
提供的内容不是对(< 2 1)
的评估,而是以<
,2
和1
作为元素的列表。除nil
之外的任何内容都是真值,因此如果您想测试一下,请尝试:(-if '(< 2 1) 'true 'false) ;==> true
。
所以在这种情况下你的测试是错误的。您的宏中存在错误:
(-if (progn (print "ONLY ONE TIME!") x) nil t)
; ==> (not x), but it prints "ONLY ONE TIME!" twice!
在创建宏时,你应该总是确保参数只被评估一次,它们被评估的顺序是参数顺序,因为commonlisper会期望它,并且无论用户使用什么符号,代码都不会失败。最后一个要求使用gensym
来确保卫生。
要修复宏错误,您需要扩展为评估谓词的let表单,以便副作用不会进行两次。此变量的名称必须与gensym
或使用gensym
的广告一致,例如热门图书中的only-once
或with-gensyms
。一本优秀的书Practical Common Lisp可以免费阅读,它有一部分关于宏和common errors and how to fix them
答案 1 :(得分:2)
您的-if-test
代码根本不起作用。
CL-USER 18 > (pprint (macroexpand '(-if test 'true 'false)))
(LET ((#:OR-SUBFORM-RESULT1129 (AND TEST 'TRUE)))
(IF #:OR-SUBFORM-RESULT1129
#:OR-SUBFORM-RESULT1129
(OR (AND (NOT TEST) 'FALSE))))
CL-USER 19 > (let ((test '(> 2 1))) (-if test 'true 'false))
TRUE
CL-USER 20 > (let ((test '(< 1 2))) (-if test 'true 'false))
TRUE
您的代码不会测试条件。它试图测试变量test
是真还是假。
如果您编写宏并使用它,则需要首先检查代码生成。
检查测试结果的方法也不符合您的要求。您对STRING=
表达式的结果执行-IF
。没有输出。另外:with-output-to-string
返回一个字符串 - 总是。 result
的值就是这个字符串。 (when result <do-this>)
然后运行<do-this>
,因为结果是一个字符串,它始终为true。在Common Lisp中,除了NIL之外的每个值都是真的。
通常,如果您确实要测试具有不同参数的宏,则实际上需要运行宏扩展。因此,您需要在测试时生成代码并在其上运行EVAL
。