Валидация числовых аргументов в Clojure - PullRequest
8 голосов
/ 22 декабря 2011

У меня есть функция clojure:

(defn f [arg1 arg2]
  ...)

Я хотел бы проверить, являются ли arg1 и arg2 числовыми (должны проходить только числовые типы, а не числовые строки).Есть, конечно, целая куча способов сделать это, но я бы хотел сделать это настолько идиоматически, насколько это возможно.Предложения?

Редактировать: Я знаю о :pre.Будем благодарны за любые комментарии относительно того, является ли это подходящим / необходимым способом справиться с этим.

Ответы [ 5 ]

13 голосов
/ 22 декабря 2011

Предварительные условия могут сделать это:

(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 для документов.

4 голосов
/ 22 декабря 2011

Функция number? звучит так, как вам нужно.Может быть, тест (and (number? arg1) (number? arg2)).

Некоторое время назад Брайан Карпер предложил макрос и серию функций для использования при проверке различных типов числовых аргументов:

;; Suggested by Brian Carper at:
;;/1196647/dolzhen-li-ya-ispolzovat-funktsiy-ili-makros-dlya-proverki-argumentov-v-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])
  ...
)
1 голос
/ 02 января 2013

Я изобрел Ужасно для такой цели!

(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")
0 голосов
/ 04 января 2018

я нашел это при поиске того же самого:

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

0 голосов
/ 23 декабря 2011
(defn add [^:number a ^:number b] (+ a b))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...