我应该使用函数或宏来验证Clojure中的参数吗? [英] Should I use a function or a macro to validate arguments in Clojure?

查看:144
本文介绍了我应该使用函数或宏来验证Clojure中的参数吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一组数字函数在Clojure,我想验证的参数。函数期望有许多类型的参数,例如正整数,百分比,数字序列,非零数字序列等等。我可以验证任何单个函数的参数:


  1. 将验证码写入
    函数。

  2. 编写一般的
    目的函数,传递
    参数和期望的类型。

  3. 编写通用宏,
    它的参数和
    预期类型。

  4. 其他我没有想过。

Larry的一些 Lisp代码猎人是#3的一个很好的例子。 (查找 test-variables 宏。)



我的直觉是宏是更合适的,因为控制评估和潜在的编译时计算,而不是在运行时全部。但是,我没有遇到一个用例的代码,我写的似乎需要它。



有没有建议?

解决方案

Clojure对 fn 的前后条件已经有了(未记录的,可能是主体更改的)支持。

  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语句。在我看来,多方法会更好。

 (defmacro assert * [val test] 
`(let [result#〜test] ;; SO的语法高亮是可怕的
(when(not result#)
(throw(Exception。
(strTest failed: test)
for(quote〜val)=〜val)))))

(defmulti validate *(fn [val test] test))

(defmethod validate *:非零[x _]
(assert * x(not = x 0)))

(defmethod validate *:even [x _]
(assert * x(even?x)))

(defn validate [& tests]
(doseq [test tests] b
$ b(defn divide [xy]
(validate [y:non-zero] [x:even])
(/ xy))



然后:

  (divide 1 0)
;评估中止。
;测试失败:(not = x 0)for x = 0
; [Thrown class java.lang.Exception]

user> (divide 5 1)
;评估中止。
;测试失败:(even?x)for x = 5
; [Thrown class java.lang.Exception]

user> (divide 6 2)
3


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:

  1. Writing validation code right into the function.
  2. Writing a general purpose function, passing it the arguments and expected types.
  3. Writing a general purpose macro, passing it the arguments and expected types.
  4. Others I haven't thought of.

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.

Any suggestions?

解决方案

Clojure already has (undocumented, maybe subject-to-change) support for pre- and post-conditions on fns.

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]

Kind of ugly though.

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))

Then:

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中的参数吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆