clojure
clojure.spec
サーチ…
構文
- ::は、名前空間限定キーワードの省略形です。例えば、私たちが名前空間にいるなら、user :: fooはuser / fooの短縮形です。
- #:または# - マップ内のキーを名前空間で修飾するためのマップリテラル構文
備考
Clojure 仕様は、バージョン1.9で入手可能なclojure用の新しい仕様/コントラクトライブラリです。
仕様は、ドキュメント、データの検証、テスト用データの生成など、さまざまな方法で活用されています。
述部を仕様として使用する
どの述語関数も仕様として使用できます。ここに簡単な例があります:
(clojure.spec/valid? odd? 1)
;;=> true
(clojure.spec/valid? odd? 2)
;;=> false
valid?
関数はspecと値をとり、値が仕様に準拠する場合はtrueを返し、そうでない場合はfalseを返します。
もう1つの興味深い述語はメンバーシップです:
(s/valid? #{:red :green :blue} :red)
;;=> true
fdef:関数の仕様書を書く
次のような機能があるとします。
(defn nat-num-count [nums] (count (remove neg? nums)))
この関数の仕様を書くには、同じ名前の関数仕様を定義します。
(clojure.spec/fdef nat-num-count
:args (s/cat :nums (s/coll-of number?))
:ret integer?
:fn #(<= (:ret %) (-> % :args :nums count)))
:args
は、引数名に対応するキーワードラベルとそれに対応するspecによって引数のシーケンスを記述するregex specをとる。 :args
が正規表現で指定するスペックは、関数に対して複数のアライメントをサポートすることを要求します。 :ret
関数の戻り値の仕様を指定します。
:fn
は:args
と:ret
間の関係を制約する仕様です。これは、test.checkを実行するときにプロパティとして使用されます。それは単一の引数で呼び出されます:二つのキー:: :args
(関数への適合した引数)と:ret
(関数の適合戻り値)を持つマップ。
仕様の登録
仕様として機能する述語に加えて、 clojure.spec/def
を使用して仕様をグローバルに登録することができます。 def
は、登録されているスペックがnamespace修飾キーワードで指定されていることを要求します。
(clojure.spec/def ::odd-nums odd?)
;;=> :user/odd-nums
(clojure.spec/valid? ::odd-nums 1)
;;=> true
(clojure.spec/valid? ::odd-nums 2)
;;=> false
一旦登録されると、仕様はClojureプログラムのどこでもグローバルに参照できます。
::odd-nums
構文は、 :user/odd-nums
省略形です。 user
空間にいると仮定します。 ::
は現在のネームスペースに先行するシンボルを修飾します。
述語を渡すのではなく、スペック名を渡すことができvalid?
それと同じように動作します。
clojure.spec /と&clojure.spec /または
clojure.spec/and
& clojure.spec/or
使用して、複数の仕様や述語を使用して、より複雑な仕様を作成することができます。
(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
1点の有意な差で、同様に動作します。 an or
specを定義するときは、可能な各ブランチにキーワードを付ける必要があります。これは、エラーメッセージで失敗する特定のブランチを提供するために使用されます。
(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
or
を使用して仕様を適合させる場合、適合する仕様が返されます。
(clojure.spec/conform ::big-or-small 5)
;; => [:small 5]
仕様の記録
あなたは次のようにレコードを指定することができます:
(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
将来のある時点で、レコードの名前空間によるレコードキーの適格性確認のためのリーダー構文またはビルトインサポートが導入される可能性があります。このサポートはすでにマップに存在します。
地図の仕様
地図にはどのキーを表示するかを指定してマップを指定できます。
(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
はマップに存在する必要があるキーのベクトルです。次のような追加のオプションを指定することができます:opt
、オプションのキーのベクトル。
これまでの例では、名前のキーが名前空間で修飾されている必要があります。しかし、マップキーが不適格であることは一般的です。この場合、 clojure.spec
は次のものを提供します:reqと:optは、非修飾キーの対応:req-un
と:opt-un
です。非修飾キーを使用した同じ例を次に示します。
(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
:req-un
ベクトルで提供されている仕様が、まだ修飾されたものとしてどのように提供されているかに注目してください。 clojure.specは、値を順守しているときにマップ内の未修飾バージョンを自動的に確認します。
名前空間マップリテラル構文を使用すると、マップのすべてのキーを単一の名前空間で簡潔に修飾することができます。例えば:
(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
特殊な#:
reader構文に注目してください。これに続いて、すべてのマップキーを修飾する名前空間でこれを行います。これらは、提供された名前空間に対応する仕様と照合されます。
コレクション
コレクションにはさまざまな方法でコレクションを指定できます。 coll-ofはコレクションを指定し、いくつかの追加の制約を提供します。ここに簡単な例があります:
(clojure.spec/valid? (clojure.spec/coll-of int?) [1 2 3])
;; => true
(clojure.spec/valid? (clojure.spec/coll-of int?) '(1 2 3))
;; => true
制約オプションは、コレクションの主な仕様/述語に従います。コレクションの型を:kind
ように制限することができます:
(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
渡されたコレクションはベクトルではないため、上記はfalseです。
(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
上記の値は、セット内のすべての要素がintではないため、falseです。
いくつかの方法でコレクションのサイズを制限することもできます。
(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
コレクション内の要素の一意性を強制することもでき: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
は、シーケンス内のすべての要素coll-of
確実にチェックされるようにします。大規模なコレクションの場合、これは非常に非効率的です。 every
同じように動作coll-of
それだけのサンプルを除いて、適合性からの配列要素の数が比較的少なく。これは大規模なコレクションでうまくいきます。ここに例があります:
(clojure.spec/valid? (clojure.spec/every int? :distinct true) [1 2 3 4 5])
;; => true
map-of
はcoll-of
map-of
と似ていますが、マップcoll-of
場合は似ています。マップにはキーと値の両方があるので、キーの仕様と値の仕様の両方を指定する必要があります。
(clojure.spec/valid? (clojure.spec/map-of keyword? string?) {:red "red" :green "green"})
;; => true
coll-of
と同様coll-of
、 map-of
はすべてのマップキー/値の適合map-of
チェックします。大きな地図の場合、これは非効率的です。同様coll-of
、 map-of
供給every-kv
効率的に大規模なマップから値の比較的小さな数をサンプリングします:
(clojure.spec/valid? (clojure.spec/every-kv keyword? string?) {:red "red" :green "green"})
;; => true
シーケンス
specは任意の配列を記述して使用できます。これは、数多くの正規表現仕様演算を介してこれをサポートします。
(clojure.spec/valid? (clojure.spec/cat :text string? :int int?) ["test" 1])
;;=> true
cat
は、シーケンスを記述するために使用される各仕様のラベルが必要です。 catは、一連の要素とそれぞれの仕様を記述します。
alt
は、シーケンス内の特定の要素に対して可能な仕様の数から選択するために使用されます。例えば:
(clojure.spec/valid? (clojure.spec/cat :text-or-int (clojure.spec/alt :text string? :int int?)) ["test"])
;;=> true
alt
では、各スペックにキーワードでラベルを付ける必要があります。
正規表現シーケンスは、任意の複雑なシーケンス記述仕様を作成するための非常に興味深く強力な方法で構成することができます。ここに少し複雑な例があります:
(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
ここで::complex-seq
は、1つ以上の要素のシーケンスを検証します。最初の要素はintで、2番目の要素はキーワードからintのマップです。