Buscar..


Sintaxis

  • :: es una abreviatura de palabra clave calificada para el espacio de nombres. Por ejemplo, si estamos en el espacio de nombres de usuario: :: foo es una abreviatura de: user / foo
  • #: o # - sintaxis literal del mapa para calificar claves en un mapa por un espacio de nombres

Observaciones

Clojure spec es una nueva biblioteca de especificación / contratos para clojure disponible a partir de la versión 1.9.

Las especificaciones se aprovechan de varias maneras, incluida la inclusión en la documentación, la validación de datos, la generación de datos para pruebas y más.

Usando un predicado como una especificación

Cualquier función de predicado se puede utilizar como una especificación. Aquí hay un ejemplo simple:

(clojure.spec/valid? odd? 1)
;;=> true

(clojure.spec/valid? odd? 2)
;;=> false

el valid? la función tomará una especificación y un valor y devolverá verdadero si el valor se ajusta a la especificación y falso de lo contrario.

Otro predicado interesante es la membresía establecida:

(s/valid? #{:red :green :blue} :red) 
;;=> true

fdef: escribiendo una especificación para una función

Digamos que tenemos la siguiente función:

(defn nat-num-count [nums] (count (remove neg? nums)))

Podemos escribir una especificación para esta función definiendo una especificación de función con el mismo nombre:

(clojure.spec/fdef nat-num-count
        :args (s/cat :nums (s/coll-of number?))
        :ret integer?
        :fn #(<= (:ret %) (-> % :args :nums count)))

:args toma una especificación de expresiones regulares que describe la secuencia de argumentos por una etiqueta de palabra clave correspondiente al nombre del argumento y una especificación correspondiente. La razón por la que la especificación requerida por :args es una especificación de expresiones regulares es para admitir múltiples aridades para una función. :ret especifica una especificación para el valor de retorno de la función.

:fn es una especificación que restringe la relación entre :args y :ret . Se utiliza como una propiedad cuando se ejecuta a través de test.check. Se llama con un solo argumento: un mapa con dos claves:: :args (los argumentos conformes a la función) y :ret (el valor de retorno conformado de la función).

Registro de una especificación

Además de los predicados que funcionan como especificaciones, puede registrar una especificación global usando clojure.spec/def . def requiere que una especificación que se está registrando sea nombrada por una palabra clave calificada para el espacio de nombres:

(clojure.spec/def ::odd-nums odd?)
;;=> :user/odd-nums

(clojure.spec/valid? ::odd-nums 1)
;;=> true
(clojure.spec/valid? ::odd-nums 2)
;;=> false

Una vez registrada, una especificación puede ser referenciada globalmente en cualquier lugar en un programa de Clojure.

La sintaxis de ::odd-nums es una abreviatura de :user/odd-nums , asumiendo que estamos en el espacio de nombres del user . :: calificará el símbolo que precede con el actual nombre.

En lugar de pasar el predicado, ¿podemos pasar el nombre de la especificación a valid? , y funcionará de la misma manera.

clojure.spec / y & clojure.spec / or

clojure.spec/and & clojure.spec/or puede usarse para crear especificaciones más complejas, utilizando múltiples especificaciones o predicados:

(clojure.spec/def ::pos-odd (clojure.spec/and odd? pos?))

(clojure.spec/valid? ::pos-odd 1)
;;=> true

(clojure.spec/valid? ::pos-odd -3)
;;=> false

or funciona de manera similar, con una diferencia significativa. Al definir una or espec., Debe etiquetar cada rama posible con una palabra clave. Esto se usa para proporcionar ramas específicas que fallan en los mensajes de error:

(clojure.spec/def ::big-or-small (clojure.spec/or :small #(< % 10) :big #(> % 100)))

(clojure.spec/valid? ::big-or-small 1)
;;=> true

(clojure.spec/valid? ::big-or-small 150)
;;=> true

(clojure.spec/valid? ::big-or-small 20)
;;=> false

Al conformar una especificación utilizando or , se devolverá la especificación aplicable que hizo que el valor sea conforme:

(clojure.spec/conform ::big-or-small 5)
;; => [:small 5]

Especificaciones de registro

Puede especificar un registro de la siguiente manera:

(clojure.spec/def ::name string?)
(clojure.spec/def ::age pos-int?)
(clojure.spec/def ::occupation string?)

(defrecord Person [name age occupation])

(clojure.spec/def ::person (clojure.spec/keys :req-un [::name ::age ::occupation]))

(clojure.spec/valid? ::person (->Person "john doe" 25 "programmer"))
;;=> true

(clojure.spec/valid? ::person (->Person "john doe" "25" "programmer"))
;;=> false

En algún momento en el futuro, se puede introducir una sintaxis de lector o un soporte integrado para calificar las claves de registro por el espacio de nombres de los registros. Este soporte ya existe para los mapas.

Especificaciones del mapa

Puede especificar un mapa especificando qué claves deben estar presentes en el mapa:

(clojure.spec/def ::name string?)
(clojure.spec/def ::age pos-int?)
(clojure.spec/def ::occupation string?)

(clojure.spec/def ::person (clojure.spec/keys :req [::name ::age ::occupation]))

(clojure.spec/valid? ::person {::name "john" ::age 25 ::occupation "programmer"})
;; => true

:req es un vector de claves que deben estar presentes en el mapa. Puede especificar opciones adicionales como :opt , un vector de claves que son opcionales.

Los ejemplos hasta ahora requieren que las claves en el nombre estén calificadas para el espacio de nombres. Pero es común que las claves del mapa no estén calificadas. Para este caso, clojure.spec proporciona: req y: opt equivalentes para claves no calificadas :req-un y :opt-un . Aquí está el mismo ejemplo, con claves no calificadas:

(clojure.spec/def ::name string?)
(clojure.spec/def ::age pos-int?)
(clojure.spec/def ::occupation string?)

(clojure.spec/def ::person (clojure.spec/keys :req-un [::name ::age ::occupation]))

(clojure.spec/valid? ::person {:name "john" :age 25 :occupation "programmer"})
;; => true

Observe cómo las especificaciones proporcionadas en el vector :req-un aún calificadas. clojure.spec, confirmará automáticamente las versiones no calificadas en el mapa al conformar los valores.

La sintaxis literal del mapa de espacio de nombres le permite calificar de manera sucinta todas las claves de un mapa por un solo espacio de nombres. Por ejemplo:

(clojure.spec/def ::name string?)
(clojure.spec/def ::age pos-int?)
(clojure.spec/def ::occupation string?)

(clojure.spec/def ::person (clojure.spec/keys :req [::name ::age ::occupation]))

(clojure.spec/valid? ::person #:user{:name "john" :age 25 :occupation "programmer"})
;;=> true

Fíjate en la #: especial #: sintaxis del lector. Seguimos esto con el espacio de nombres por el que deseamos calificar todas las claves de mapa. Estos se compararán con las especificaciones correspondientes al espacio de nombres proporcionado.

Colecciones

Puede especificar colecciones de varias maneras. coll-of le permite especificar colecciones y proporcionar algunas restricciones adicionales. Aquí hay un ejemplo simple:

(clojure.spec/valid? (clojure.spec/coll-of int?) [1 2 3])
;; => true

(clojure.spec/valid? (clojure.spec/coll-of int?) '(1 2 3))
;; => true

Las opciones de restricción siguen la especificación / predicado principal de la colección. Puede restringir el tipo de colección con :kind como este:

(clojure.spec/valid? (clojure.spec/coll-of int? :kind vector?) [1 2 3])
;; => true

(clojure.spec/valid? (clojure.spec/coll-of int? :kind vector?) '(1 2 3))
;; => false

Lo anterior es falso porque la colección pasada no es un vector.

(clojure.spec/valid? (clojure.spec/coll-of int? :kind list?) '(1 2 3))
;; => true

(clojure.spec/valid? (clojure.spec/coll-of int? :kind set?) #{1 2 3})
;; => true

(clojure.spec/valid? (clojure.spec/coll-of int? :kind set?) #{1 "2" 3})
;; => false

Lo anterior es falso porque no todos los elementos en el conjunto son ints.

También puede restringir el tamaño de la colección de varias maneras:

(clojure.spec/valid? (clojure.spec/coll-of int? :kind vector? :count 3) [1 2 3])
;; => true

    (clojure.spec/valid? (clojure.spec/coll-of int? :kind vector? :count 3) [1 2])
;; => false

(clojure.spec/valid? (clojure.spec/coll-of int? :min-count 3 :max-count 5) [1 2 3])
;; => true

    (clojure.spec/valid? (clojure.spec/coll-of int? :min-count 3 :max-count 5) [1 2])
;; => false

También puede imponer la unicidad de los elementos de la colección con :distinct :

(clojure.spec/valid? (clojure.spec/coll-of int? :distinct true) [1 2])
;; => true

(clojure.spec/valid? (clojure.spec/coll-of int? :distinct true) [2 2])
;; => false

coll-of asegura que todos los elementos en una secuencia son verificados. Para grandes colecciones, esto puede ser muy ineficiente. every comporta como una coll-of , excepto que solo muestrea un número relativamente pequeño de elementos de las secuencias para su conformidad. Esto funciona bien para grandes colecciones. Aquí hay un ejemplo:

(clojure.spec/valid? (clojure.spec/every int? :distinct true) [1 2 3 4 5])
;; => true

map-of es similar a coll-of , pero para mapas. Como los mapas tienen tanto claves como valores, debe proporcionar una especificación para la clave y una especificación para el valor:

(clojure.spec/valid? (clojure.spec/map-of keyword? string?) {:red "red" :green "green"})
;; => true

Al igual que la coll-of , la map-of controles cumple con todas las claves / valores del mapa. Para mapas grandes esto será ineficiente. Al igual que la coll-of , map-of suministra every-kv para muestrear de manera eficiente un número relativamente pequeño de valores de un mapa grande:

(clojure.spec/valid? (clojure.spec/every-kv keyword? string?) {:red "red" :green "green"})
;; => true

Secuencias

La especificación puede describirse y usarse con secuencias arbitrarias. Es compatible con esto a través de una serie de operaciones de especificación de expresiones regulares.

(clojure.spec/valid? (clojure.spec/cat :text string? :int int?) ["test" 1])
;;=> true

cat requiere etiquetas para cada especificación utilizada para describir la secuencia. El gato describe una secuencia de elementos y una especificación para cada uno.

alt se utiliza para elegir entre una serie de posibles especificaciones para un elemento dado en una secuencia. Por ejemplo:

(clojure.spec/valid? (clojure.spec/cat :text-or-int (clojure.spec/alt :text string? :int int?)) ["test"])
;;=> true

alt también requiere que cada especificación esté etiquetada por una palabra clave.

Las secuencias Regex se pueden componer de formas muy interesantes y potentes para crear especificaciones de descripciones de secuencias arbitrariamente complejas. Aquí hay un ejemplo un poco más complejo:

(clojure.spec/def ::complex-seq (clojure.spec/+ (clojure.spec/cat :num int? :foo-map (clojure.spec/map-of keyword? int?))))
(clojure.spec/valid? ::complex-seq [0 {:foo 3 :baz 1} 4 {:foo 4}])
;;=> true

Aquí ::complex-seq validará una secuencia de uno o más pares de elementos, el primero es un int y el segundo es un mapa de palabra clave para int.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow