【问题标题】:Format both an expression and its result without eval在不使用 eval 的情况下格式化表达式及其结果
【发布时间】:2015-04-30 17:01:22
【问题描述】:

我正在尝试格式化任意表达式,比如(+ 2 3),同时,它的结果是5

我有以下几点:

(defun expr-and-result (expr)
  (format t "~a returns ~a~%" expr (eval expr)))

CL-USER> (expr-and-result '(+ 2 3))
  (+ 2 3) returns 5

虽然使用eval是一件简单的事情,但我很好奇如果没有它是否可以实现这种效果(因为我听说很多要避免使用eval)。

我知道引用参数是必要的,因为否则给定的表达式将作为调用expr-and-result 的第一步进行评估,并且只有它的结果可以在expr-and-result 中使用。因此,任何可能的解决方案都需要引用输入,对吧?

我对宏进行了一些思考,但我觉得这是我正在寻找的错误方法。

编辑:我的意图是构建一个简单的测试套件,例如:

(progn
  (mapcar #'expr-and-result
          '((= (my-remainder 7 3) 1)
            (= (my-remainder 7 3) 2)))
  'end-of-tests)

Outputs: 
(= (MY-REMAINDER 7 3) 1) returns T
(= (MY-REMAINDER 7 3) 2) returns NIL
END-OF-TESTS

阅读 Paulo 的评论后,似乎eval 是我所用的最短和最干净的解决方案。

【问题讨论】:

  • 这取决于您打算如何使用它。如果您正在开发一个详细的调试宏,那么您应该生成(format ...) 表达式,引用第一个参数而不是第二个参数。如果这是用于 REPL 或某些解释器,那很好。 eval 的编译替代方案是 (funcall (compile nil `(lambda () ,expr))),但除非你真的想观察编译副作用或多次执行 expr,否则没有必要这样做。
  • @PauloMadeira 感谢您的评论,我的目的是创建一个简洁的测试套件,用于将函数输出与预期结果进行比较。 (我编辑了我的问题)。生成format 表达式听起来有点矫枉过正,但我​​觉得这将是一个比 eval 更灵活的解决方案。
  • 不要重新发明轮子:cliki.net/test%20framework
  • 有很多轮子可供选择!

标签: common-lisp eval


【解决方案1】:

这是一个简单的宏:

(defmacro exec-and-report (form)
  `(format t "~S returns ~S~%" ',form ,form))
(macroexpand '(exec-and-report (+ 1 2)))
==>
(FORMAT T "~S returns ~S~%" '(+ 1 2) (+ 1 2)) ;
T
(exec-and-report (+ 1 2))
==>
(+ 1 2) returns 3
NIL

PS。我赞同@Sylvester 的建议not to reinvent the wheel

【讨论】:

  • 我(个人)可能会将其包装在 LET 中,并以返回值结束。这也允许将其作为参数插入(但是,嘿,这完全没问题)。
猜你喜欢
  • 1970-01-01
  • 2015-04-01
  • 1970-01-01
  • 2010-09-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多