Sök…


Syntax

  • :: är ett kortfattat nyckelord-kvalificerat nyckelord. Exempelvis om vi befinner oss i namnutrymme: :: foo är ett kort för: användare / foo
  • #: eller # - karta-bokstavlig syntax för kvalificeringstangenter på en karta med ett namnområde

Anmärkningar

Clojure spec är ett nytt specifikations / kontrakt bibliotek för clojure tillgängligt från version 1.9.

Specifikationer är utnyttjade på flera sätt inklusive inkluderande i dokumentation, datavalidering, generering av data för testning och mer.

Använda ett predikat som en specifik

Varje predikatfunktion kan användas som en specifik. Här är ett enkelt exempel:

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

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

det valid? funktionen kommer att ta en spec och ett värde och returnera true om värdet överensstämmer med spec och falskt annars.

Ett annat intressant predikat är medlemskap:

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

fdef: skriva en spec för en funktion

Låt oss säga att vi har följande funktion:

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

Vi kan skriva en specifikation för den här funktionen genom att definiera en funktionsspecifikation med samma namn:

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

:args tar en regex-specifikation som beskriver sekvensen av argument med en nyckelordetikett som motsvarar argumentnamnet och motsvarande spec. Anledningen till att specifikationen krävs av :args är en regex-specifikation är att stödja flera arities för en funktion. :ret anger en specifikation för funktionens returvärde.

:fn är en spec som begränsar förhållandet mellan :args och :ret . Den används som en egenskap när den körs genom test.check. Det kallas med ett enda argument: en karta med två nycklar :args (de överensstämmande argumenten för funktionen) och :ret (funktionens överensstämmda returvärde).

Registrerar en specifik

Förutom att predikat fungerar som specifikationer kan du registrera en specifik globalt med clojure.spec/def . def kräver att en specifik som registreras namnges av ett namnområde-kvalificerat nyckelord:

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

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

När det har registrerats kan en specifik refereras globalt var som helst i ett Clojure-program.

Syntaxen ::odd-nums är en kortfattning för :user/odd-nums , förutsatt att vi befinner oss i user . :: kommer att kvalificera den symbol som den föregår med den nuvarande namnutrymmet.

I stället för att överföra predikatet, kan vi överföra specifikamnet till valid? , och det fungerar på samma sätt.

clojure.spec / och & clojure.spec / eller

clojure.spec/and & clojure.spec/or kan användas för att skapa mer komplexa specifikationer med flera specifikationer eller predikat:

(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 fungerar på liknande sätt, med en betydande skillnad. När du definierar en or specifik måste du märka varje möjlig gren med ett nyckelord. Detta används för att tillhandahålla specifika grenar som misslyckas i felmeddelanden:

(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

Vid överensstämmelse med en specifik med or , kommer den tillämpliga specifikationen att returneras vilket gjorde att värdet överensstämmer med:

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

Spela in specifikationer

Du kan ange en post på följande sätt:

(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

Vid någon tidpunkt i framtiden kan en läsersyntax eller inbyggt stöd för kvalificering av inspelningsnycklar med postens namnutrymme införas. Detta stöd finns redan för kartor.

Kartuppgifter

Du kan specificera en karta genom att ange vilka nycklar som ska finnas på kartan:

(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 är en vektor av nycklar som krävs för att finnas på kartan. Du kan ange ytterligare alternativ som :opt , en vektor med nycklar som är valfria.

Exemplen hittills kräver att nycklarna i namnet är namnutrymmeskvalificerade. Men det är vanligt att kartnycklar är okvalificerade. För detta fall tillhandahåller clojure.spec : req och: opt ekvivalenter för okvalificerade nycklar :req-un och :opt-un . Här är samma exempel med okvalificerade nycklar:

(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

Lägg märke till hur specifikationerna i :req-un vektorn fortfarande är kvalificerade. clojure.spec, kommer automatiskt att bekräfta de okvalificerade versionerna på kartan när värdena överensstämmer.

bokstavlig syntax med namnutrymmet gör att du snabbt kan kvalificera alla nycklar på en karta med ett enda namnutrymme. Till exempel:

(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

Lägg märke till den speciella #: läsersyntaxen. Vi följer detta med det namnutrymme som vi vill kvalificera alla kartnycklar med. Dessa kommer sedan att kontrolleras mot specifikationerna som motsvarar det angivna namnområdet.

samlingar

Du kan specificera samlingar på flera sätt. coll-of låter dig specificera samlingar och ge några ytterligare begränsningar. Här är ett enkelt exempel:

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

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

Begränsningsalternativ följer huvudspecifikationen / predikatet för samlingen. Du kan begränsa kollektionstypen med :kind så här:

(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

Ovanstående är falskt eftersom samlingen som skickas in inte är en vektor.

(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

Ovanstående är falskt eftersom inte alla element i uppsättningen är ints.

Du kan också begränsa kollektionens storlek på några sätt:

(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

Du kan också säkerställa att elementen i samlingen är unika med :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 säkerställer att alla element i en sekvens kontrolleras. För stora samlingar kan detta vara mycket ineffektivt. every uppför sig precis som en coll-of , förutom att det bara provar ett relativt litet antal av sekvensernas element från för överensstämmelse. Detta fungerar bra för stora samlingar. Här är ett exempel:

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

map-of liknar coll-of , men för kartor. Eftersom kartor har både nycklar och värden måste du ange både en specifik för nyckeln och en specifik för värdet:

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

Liksom coll-of , map-of kontroller överensstämmelse av alla kart nyckel / värden. För stora kartor kommer detta att vara ineffektivt. Liksom coll-of map-of leveranser every-kv för effektivt sampling av ett relativt litet antal värden från en stor karta:

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

sekvenser

spec kan beskriva och användas med godtyckliga sekvenser. Det stöder detta via ett antal regex spec-operationer.

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

cat kräver etiketter för varje spec som används för att beskriva sekvensen. katt beskriver en sekvens av element och en specifik för var och en.

alt används för att välja mellan ett antal möjliga specifikationer för ett givet element i en sekvens. Till exempel:

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

alt kräver också att varje spec är märkt med ett nyckelord.

Regex-sekvenser kan komponeras på några mycket intressanta och kraftfulla sätt att skapa godtyckligt komplexa sekvensbeskrivande specifikationer. Här är ett lite mer komplex exempel:

(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

Här ::complex-seq kommer att validera en sekvens av ett eller flera par av element, den första är en int och den andra är en karta med nyckelord till int.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow