2011-12-22 11 views
8

que tiene una función clojure:validación de argumentos numéricos en Clojure

(defn f [arg1 arg2] 
    ...) 

me gustaría probar si arg1 y arg2 son numéricos (sólo tipos numéricos deben pasar - no numéricamente cadenas con formato). Hay, por supuesto, muchas formas de hacerlo, pero me gustaría hacerlo lo más idiomáticamente posible. Sugerencias?

Editar: Sé de :pre. Cualquier comentario sobre si es o no una forma apropiada/necesaria para manejar esto sería apreciado.

Respuesta

12

Las condiciones previas pueden hacer eso:

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

(test 1 2) 
=> 3 

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

Ver http://clojure.org/special_forms#toc9 de documentos.

+0

Gracias, Justin. ¿Sería una mala forma para mí no usar ': pre' y usar algo más en su lugar? Me encuentro en una situación en la que es un poco difícil (aunque creo que tu respuesta es probablemente la que la mayoría de la gente querrá). – charleslparker

+0

No se puede emitir un juicio sin más detalles. ¿Por qué es difícil usar ': pre'? –

4

La función number? suena como lo que necesita. Tal vez una prueba de (and (number? arg1) (number? arg2)).

Hace un tiempo, Brian Carper sugirió una serie de macros y de funciones para usar en la validación de los diferentes tipos de argumentos numéricos:

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

Esto ha demostrado ser muy flexible en mi experiencia. Como puede ver, es fácil extender el mulitmethod a nuevas pruebas.

uso sería algo así como:

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

Gracias, sí, parece lo que necesito. ¿Qué pasa con ': pre'? ¿Crees que está bien no usar eso aquí? – charleslparker

+0

@mistertero: ': pre' no existía cuando hice la pregunta original (Clojure pre-1.0) y me he acostumbrado a la macro. Creo que usar la macro puede llevar a menos tipeo; Imagine tener que escribir la condición para ': nonzero-numseq' por ejemplo. ': pre' es probablemente mejor para condiciones únicas y muy específicas, como probar valores específicos en lugar de clases enteras de números. – clartaq

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

he inventado Dire para tal fin!

(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

he encontrado this mientras busca lo mismo:

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

y luego se puede utilizar de esa manera:

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

espero que ayude! (Todos los créditos van a la respuesta original de Shantanu Kumar en grupos de google)

Cuestiones relacionadas