问题描述
我在 Clojure 中有一组数字函数,我想验证其参数.函数期望的参数有多种类型,例如正整数、百分比、数字序列、非零数字序列等.我可以通过以下方式验证任何单个函数的参数:
I have a group of numeric functions in Clojure that I want to validate the arguments for. There are numerous types of arguments expected by the functions, such as positive integers, percentages, sequences of numbers, sequences of non-zero numbers, and so on. I can validate the arguments to any individual function by:
- 将验证代码直接写入功能.
- 写一般目的函数,将其传递给参数和预期类型.
- 编写一个通用宏,传递参数和预期的类型.
- 其他我没有想到的.
Larry Hunter 的一些 Lisp 代码是#3 的一个很好的例子.(查找 test-variables
宏.)
Some Lisp code by Larry Hunter is a nice example of #3. (Look for the test-variables
macro.)
我的直觉是宏更合适,因为它可以控制评估,并且有可能在编译时进行计算,而不是在运行时全部完成.但是,对于我正在编写的似乎需要它的代码,我还没有遇到过用例.我想知道写这样一个宏是否值得.
My intuition is that a macro is more appropriate because of the control over evaluation and the potential to do compile-time computation rather than doing it all at run time. But, I haven't run into a use case for the code I'm writing that seems to require it. I'm wondering if it is worth the effort to write such a macro.
有什么建议吗?
推荐答案
Clojure 已经(未记录,可能会更改)支持 fn
上的前置和后置条件.
Clojure already has (undocumented, maybe subject-to-change) support for pre- and post-conditions on fn
s.
user> (defn divide [x y]
{:pre [(not= y 0)]}
(/ x y))
user> (divide 1 0)
Assert failed: (not= y 0)
[Thrown class java.lang.Exception]
虽然有点丑.
我可能会写一个宏,这样我就可以以简洁的方式报告哪些测试失败(逐字引用并打印测试).您链接到的 CL 代码在那个巨大的 case 语句中看起来很糟糕.在我看来,多方法在这里会更好.你可以很容易地自己把这样的东西放在一起.
I'd probably write a macro just so I could report which tests failed in a succinct way (quote and print the test literally). The CL code you linked to looks pretty nasty with that enormous case statement. Multimethods would be better here in my opinion. You can throw something like this together pretty easily yourself.
(defmacro assert* [val test]
`(let [result# ~test] ;; SO`s syntax-highlighting is terrible
(when (not result#)
(throw (Exception.
(str "Test failed: " (quote ~test)
" for " (quote ~val) " = " ~val))))))
(defmulti validate* (fn [val test] test))
(defmethod validate* :non-zero [x _]
(assert* x (not= x 0)))
(defmethod validate* :even [x _]
(assert* x (even? x)))
(defn validate [& tests]
(doseq [test tests] (apply validate* test)))
(defn divide [x y]
(validate [y :non-zero] [x :even])
(/ x y))
那么:
user> (divide 1 0)
; Evaluation aborted.
; Test failed: (not= x 0) for x = 0
; [Thrown class java.lang.Exception]
user> (divide 5 1)
; Evaluation aborted.
; Test failed: (even? x) for x = 5
; [Thrown class java.lang.Exception]
user> (divide 6 2)
3
这篇关于我应该使用函数还是宏来验证 Clojure 中的参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!