首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >` of‘-不可理解行为的另一种实现

` of‘-不可理解行为的另一种实现
EN

Stack Overflow用户
提问于 2014-05-20 17:33:44
回答 2查看 120关注 0票数 1

我想使用布尔逻辑和宏编写自己的if。我想出了以下实现:

代码语言:javascript
复制
(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)))

我在几个案例上手动测试了它,它如预期的那样工作。但是后来我写了一个简单的测试函数来执行一系列测试,得到了一个我仍然不能理解的结果。我写的函数是这样的:

代码语言:javascript
复制
(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))))

当我运行测试时,我得到以下输出:

代码语言:javascript
复制
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调试它,结果确实与独立执行的结果不同。我怀疑我错过了一些关键的执行细节或类似的东西。有人能给我解释一下这里发生了什么吗?真的很感谢你的帮助。

附注:我的实现是Clozure CL。

EN

回答 2

Stack Overflow用户

发布于 2014-05-20 18:50:58

测试用例就是数据。您提供给-if的不是对(< 2 1)的评估,而是包含<21元素的列表。除了nil以外的任何东西都是真实值,所以如果你想测试你所做的:(-if '(< 2 1) 'true 'false) ;==> true

因此,在这种情况下,您的测试是错误的。不过,您的宏中存在错误:

代码语言:javascript
复制
(-if (progn (print "ONLY ONE TIME!") x) nil t)
; ==> (not x), but it prints "ONLY ONE TIME!" twice!

在创建宏时,您应该始终确保参数只计算一次,计算它们的顺序是按照参数顺序进行的,因为通用词法分析器会预期到这一点,并且无论用户使用什么符号,代码都不会失败。最后一个要求使用gensym来确保卫生。

要修复宏错误,您需要扩展为计算谓词的let形式,这样副作用就不会重复出现。对于gensym或使用gensym的宏而言,此变量的名称必须是唯一的,如流行书籍中的only-oncewith-gensymsPractical Common Lisp是一本很好的书,它是免费阅读的,其中有一部分是关于宏和common errors and how to fix them

票数 2
EN

Stack Overflow用户

发布于 2014-05-20 18:51:05

您的-if-test代码根本不起作用。

代码语言:javascript
复制
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是否为真或假。

如果您编写了一个宏并使用它,您需要首先检查代码生成。

  • 生成的代码看起来像您想要的吗?
  • 在生成的代码中是否多次执行了操作?
  • 在生成的代码中是否按正确的顺序执行了操作?
  • 生成的代码中是否有不应该出现的变量或函数符号?

< code >H112是否正确处理了宏参数?

此外,检查测试结果的方法并不能实现您想要的结果。对-IF表达式的结果执行STRING=。而不是输出结果。另外:with-output-to-string总是返回一个字符串。result的值就是这个字符串。然后,(when result <do-this>)运行<do-this>,因为result是一个字符串,这始终是真的。在Common Lisp中,除NIL以外的所有值都为true。

一般来说,如果你真的想用不同的参数测试一个宏,你实际上需要运行宏展开。因此,您需要在测试时生成代码并在其上运行EVAL

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23756037

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档