【问题标题】:Validation of Numeric Arguments in ClojureClojure 中数值参数的验证
【发布时间】:2011-12-22 16:35:53
【问题描述】:

我有一个clojure函数:

(defn f [arg1 arg2]
  ...)

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

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

【问题讨论】:

    标签: validation clojure arguments


    【解决方案1】:

    前置条件可以做到这一点:

    (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

    【讨论】:

    • 谢谢,贾斯汀。不使用:pre 而是使用其他东西对我来说会是不好的形式吗?我碰巧遇到了一个有点困难的情况(尽管我认为您的答案可能是大多数人想要的答案)。
    • 没有更多细节无法做出判断。为什么使用:pre 很困难?
    【解决方案2】:

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

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

    用法类似于:

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

    【讨论】:

    • 谢谢 - 是的,这似乎是我需要的。 :pre 呢?您认为这里不使用它可以吗?
    • @mistertero::pre 在我问最初的问题(Clojure pre-1.0)时不存在,我已经习惯了宏。我认为使用宏可以减少打字;例如,想象一下必须输入:nonzero-numseq 的条件。 :pre 可能更适合一次性、非常具体的条件,例如测试特定值而不是整个数字类别。
    【解决方案3】:

    我为此发明了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")
    

    【讨论】:

      【解决方案4】:

      我在搜索相同内容时找到了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))
      

      希望对你有帮助! (所有学分归于 google 群组中 Shantanu Kumar 的原始答案)

      【讨论】:

        【解决方案5】:
        (defn add [^:number a ^:number b] (+ a b))
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-12-11
          • 2010-10-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多