Suche…


Syntax

  • :: ist eine Abkürzung für ein mit dem Namensraum qualifiziertes Schlüsselwort. ZB wenn wir im Namespace Benutzer sind: :: foo ist eine Abkürzung für: user / foo
  • #: oder # - Map-Literal-Syntax für qualifizierende Schlüssel in einer Map anhand eines Namespaces

Bemerkungen

Clojure spec ist eine neue Spezifikations- / Vertragsbibliothek für Clojure, die ab Version 1.9 verfügbar ist.

Spezifikationen werden auf verschiedene Weise genutzt, z. B. zur Dokumentation, zur Datenvalidierung, zum Generieren von Testdaten und mehr.

Verwenden eines Prädikats als Spezifikation

Jede Prädikatfunktion kann als Spezifikation verwendet werden. Hier ist ein einfaches Beispiel:

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

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

das valid? function nimmt eine Spezifikation und einen Wert und gibt true zurück, wenn der Wert der Spezifikation entspricht, andernfalls false

Ein weiteres interessantes Prädikat ist die Set-Mitgliedschaft:

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

fdef: Eine Spezifikation für eine Funktion schreiben

Nehmen wir an, wir haben folgende Funktion:

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

Wir können eine Spezifikation für diese Funktion schreiben, indem Sie eine Funktionsspezifikation mit demselben Namen definieren:

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

:args nimmt eine Regex-Spezifikation, die die Folge von Argumenten durch eine Schlüsselwortbezeichnung beschreibt, die dem Argumentnamen und einer entsprechenden Spezifikation entspricht. Der Grund, warum die Spezifikation von :args eine Regex-Spezifikation ist, ist die Unterstützung mehrerer Arities für eine Funktion. :ret gibt eine Spezifikation für den Rückgabewert der Funktion an.

:fn ist eine Spezifikation, die die Beziehung zwischen den :args und der :ret . Sie wird beim Durchlaufen von test.check als Eigenschaft verwendet. Es wird mit einem einzigen Argument aufgerufen: einer Map mit zwei Schlüsseln :args (die konformen Argumente der Funktion) und :ret (der konforme Rückgabewert der Funktion).

Eine Spezifikation registrieren

Zusätzlich zu Prädikaten, die als Spezifikationen dienen, können Sie eine Spezifikation global mit clojure.spec/def registrieren. def setzt def , dass eine zu registrierende Spezifikation durch ein mit Namensraum qualifiziertes Schlüsselwort benannt wird:

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

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

Nach der Registrierung kann eine Spezifikation überall in einem Clojure-Programm weltweit referenziert werden.

Die ::odd-nums Syntax ist eine Abkürzung für :user/odd-nums , vorausgesetzt, wir befinden uns im user . :: qualifiziert das Symbol, das vor dem aktuellen Namen steht.

Anstatt das Prädikat zu übergeben, können wir den Spezifikationsnamen als valid? und es wird genauso funktionieren.

clojure.spec / und & clojure.spec / oder

clojure.spec/and & clojure.spec/or können zum Erstellen komplexer Spezifikationen verwendet werden, wobei mehrere Spezifikationen oder Prädikate verwendet werden:

(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 funktioniert ähnlich, mit einem signifikanten Unterschied. Bei der Definition einer or Spezifikation müssen Sie jeden möglichen Zweig mit einem Schlüsselwort kennzeichnen. Dies wird verwendet, um bestimmte Zweige bereitzustellen, in denen Fehlernachrichten fehlschlagen:

(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

Beim Anpassen einer Spezifikation mithilfe von or wird die entsprechende Spezifikation zurückgegeben, wodurch der Wert übereinstimmt:

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

Daten aufzeichnen

Sie können einen Datensatz wie folgt angeben:

(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

Irgendwann in der Zukunft kann eine Lesersyntax oder eine integrierte Unterstützung zum Qualifizieren von Datensatzschlüsseln durch den Namensraum der Datensätze eingeführt werden. Diese Unterstützung besteht bereits für Karten.

Kartenspezifikationen

Sie können eine Karte angeben, indem Sie angeben, welche Schlüssel in der Karte vorhanden sein sollen:

(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 ist ein Vektor von Schlüsseln, die in der Karte vorhanden sein müssen. Sie können zusätzliche Optionen angeben, z. :opt , einen Vektor von Schlüsseln, die optional sind.

Die bisherigen Beispiele setzen voraus, dass die Schlüssel im Namen für den Namespace qualifiziert sind. Es ist jedoch üblich, dass Kartenschlüssel nicht qualifiziert sind. Für diesen Fall stellt clojure.spec Verfügung: req und: opt Entsprechungen für nicht qualifizierte Schlüssel clojure.spec :req-un und :opt-un . Hier das gleiche Beispiel mit unqualifizierten Schlüsseln:

(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

Beachten Sie, wie die Angaben im noch nicht qualifizierten :req-un Vektor angegeben werden. clojure.spec bestätigt automatisch die unqualifizierten Versionen in der Karte, wenn die Werte angepasst werden.

Mit der Namespace-Map-Literal-Syntax können Sie alle Schlüssel einer Map durch einen einzigen Namespace kurz qualifizieren. Zum Beispiel:

(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

Beachten Sie die spezielle #: Leser-Syntax. Wir folgen diesem mit dem Namensraum, nach dem alle Kartenschlüssel qualifiziert werden sollen. Diese werden dann mit den Angaben des angegebenen Namespaces verglichen.

Sammlungen

Sie können Sammlungen auf verschiedene Arten angeben. Mit coll-of können Sie Sammlungen spezifizieren und einige zusätzliche Einschränkungen angeben. Hier ist ein einfaches Beispiel:

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

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

Einschränkungsoptionen folgen der Hauptspezifikation / dem Prädikat für die Sammlung. Sie können den Auflistungstyp einschränken mit :kind wie folgt:

(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

Das obige ist falsch, da die übergebene Sammlung kein Vektor ist.

(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

Das obige ist falsch, da nicht alle Elemente in der Gruppe Ints sind.

Sie können die Größe der Sammlung auf verschiedene Weise einschränken:

(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

Sie können die Eindeutigkeit der Elemente in der Auflistung auch erzwingen mit :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 stellt sicher, dass alle Elemente in einer Sequenz geprüft werden. Bei großen Sammlungen kann dies sehr ineffizient sein. every verhält sich wie ein coll-of , außer dass nur eine relativ kleine Anzahl der Elemente der Sequenzen zur Konformität abgetastet wird. Dies funktioniert gut für große Sammlungen. Hier ist ein Beispiel:

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

map-of ist ähnlich wie coll-of , aber für Karten. Da Maps sowohl Schlüssel als auch Werte haben, müssen Sie sowohl eine Spezifikation für den Schlüssel als auch eine Spezifikation für den Wert angeben:

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

Wie coll-of map-of Prüfungen wird die Übereinstimmung aller Zuordnungsschlüssel / -werte mit coll-of map-of überprüft. Bei großen Karten ist dies ineffizient. Wie coll-of map-of Lieferungen werden every-kv Werte für das effiziente Abtasten einer relativ kleinen Anzahl von Werten aus einer großen Karte erstellt:

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

Sequenzen

spec kann beschreiben und mit beliebigen Sequenzen verwendet werden. Es unterstützt dies durch eine Reihe von regulären Ausdrücken.

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

cat erfordert Kennzeichnungen für jede Spezifikation, die zur Beschreibung der Sequenz verwendet wird. cat beschreibt eine Abfolge von Elementen und eine Spezifikation für jedes Element.

alt wird verwendet, um aus einer Reihe möglicher Spezifikationen für ein bestimmtes Element in einer Sequenz auszuwählen. Zum Beispiel:

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

alt erfordert auch, dass jede Spezifikation mit einem Schlüsselwort gekennzeichnet ist.

Regex-Sequenzen können auf einige sehr interessante und leistungsfähige Arten zusammengestellt werden, um beliebig komplexe sequenzbeschreibende Spezifikationen zu erstellen. Hier ist ein etwas komplexeres Beispiel:

(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

Hier wird ::complex-seq eine Sequenz von einem oder mehreren ::complex-seq validieren, wobei das erste ein int und das zweite eine Zuordnung des Schlüsselworts zu int ist.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow