在Clojure中验证数字参数

cha*_*ker 8 validation arguments clojure

我有一个clojure功能:

(defn f [arg1 arg2]
  ...)
Run Code Online (Sandbox Code Playgroud)

我想测试,如果arg1arg2是数字(仅适用于数字类型应该传递-不是数字格式的字符串).当然,有很多方法可以做到这一点,但我想尽可能地做到这一点.建议?

编辑:我知道:pre.任何关于这是否是处理这一问题的适当/必要方式的评论将不胜感激.

Jus*_*mer 13

前提条件可以做到:

(defn test [arg1 arg2]
  {:pre [(number? arg1) (number? arg2)]}
  (+ arg1 arg2))

(test 1 2)
=> 3

(test 1 "2")
=> Assert failed: (number? arg2)
Run Code Online (Sandbox Code Playgroud)

有关文档,请参见http://clojure.org/special_forms#toc9.


cla*_*taq 5

number?功能听起来像是您所需要的。也许是一个测试(and (number? arg1) (number? arg2))

不久前,Brian Carper 建议使用宏和一系列函数来验证不同类型的数字参数:

;; Suggested by Brian Carper at:
;;http://stackoverflow.com/questions/1640311/should-i-use-a-function-or-a-macro-to-validate-arguments-in-clojure

(defmacro assert* [val test]
  `(let [result# ~test]
     (when (not result#)
       (throw (IllegalArgumentException.
                (str "Test failed: " (quote ~test)
                  " for " (quote ~val) " = " ~val))))))

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

(defmethod validate* :prob [x _]
  (assert* x (and (number? x) (pos? x) (<= x 1.0))))

(defmethod validate* :posint [x _]
  (assert* x (and (integer? x) (pos? x))))

(defmethod validate* :non-negint [x _]
  (assert* x (and (integer? x) (not (neg? x)))))

(defmethod validate* :posnum [x _]
  (assert* x (and (number? x) (pos? x))))

(defmethod validate* :percentage [x _]
  (assert* x (and (number? x) (pos? x) (<= x 100))))

(defmethod validate* :numseq [x _]
  (assert* x (and (not (empty? x)) (seq? x) (every? number? x))))

(defmethod validate* :nonzero-numseq [x _]
  (assert* x (and (not (empty? x)) (seq? x) (every? #(and (number? %) (not (zero? %))) x))))

(defmethod validate* :posint-seq [x _]
  (assert* x (and (not (empty? x)) (seq? x) (every? #(and (integer? %) (pos? %)) x))))

(defmethod validate* :prob-seq [x _]
  (assert* x (and (not (empty? x)) (seq? x) (every? #(and (number? %) (pos? %) (<= % 1.0)) x))))

(defmethod validate* :default [x _]
  (throw (IllegalArgumentException.
                (str "Unrecognized validation type"))))

(defn validate [& tests]
  (doseq [test tests] (apply validate* test)))
Run Code Online (Sandbox Code Playgroud)

根据我的经验,这证明非常灵活。正如您所看到的,很容易将多重方法扩展到新的测试。

用法如下:

(defn f [arg1 arg2]
  "arg1 must be a positive integer, arg2 must be a positive number"
  (validate [arg1 :posint] [arg2 :posnum])
  ...
)
Run Code Online (Sandbox Code Playgroud)