在Clojure中验证数字参数

时间:2011-12-22 16:35:53

标签: validation clojure arguments

我有一个clojure功能:

(defn f [arg1 arg2]
  ...)

我想测试arg1arg2是否为数字(只有数字类型应该传递 - 而不是数字格式的字符串)。当然,有很多方法可以做到这一点,但我想尽可能地做到这一点。建议?

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

5 个答案:

答案 0 :(得分:13)

前提条件可以做到:

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

(test 1 2)
=> 3

(test 1 "2")
=> Assert failed: (number? arg2)

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

答案 1 :(得分:4)

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

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

用法如下:

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

答案 2 :(得分:1)

我为此目的发明了Dire

(defn f [a b]
  (+ a b))

(defprecondition f
  :args-numeric
  (fn [a b & more]
    (and (number? a) (number? b))))

(defhandler f
  {:precondition :args-numeric}
  (fn [e & args] (apply str "Failure for argument list: " (vector args))))

(supervise f "illegal" "args")

答案 3 :(得分:0)

我在搜索同一件事时找到了this

(defmacro verify-my-arg
 "Like assert, except for the following differences:
 1. does not check for *assert* flag
 2. throws IllegalArgumentException"
 [err-msg arg]
 `(if ~arg true
    (throw (IllegalArgumentException. ~err-msg))))

然后你可以这样使用它:

(defn foo [m]
 {:pre [(verify-my-arg "m must be a map" (map? m))]}
 (println m))

我希望它有所帮助! (所有学分都归到了谷歌小组中Shantanu Kumar的原始答案)

答案 4 :(得分:-1)

(defn add [^:number a ^:number b] (+ a b))