8

この回答を書いた後、私はClojure の分解言語spec次のように指定しようと思いつきました。

(require '[clojure.spec :as s])

(s/def ::binding (s/or :sym ::sym :assoc ::assoc :seq ::seq))

(s/def ::sym (s/and simple-symbol? (complement #{'&})))

順次分割部分は正規表現で簡単に指定できます (ここでは無視します) が、連想分割で行き詰まりました。最も基本的なケースは、バインド フォームからキー式へのマップです。

(s/def ::mappings (s/map-of ::binding ::s/any :conform-keys true))

しかし、Clojure にはいくつかの特別なキーも用意されています。

(s/def ::as ::sym)
(s/def ::or ::mappings)

(s/def ::ident-vec (s/coll-of ident? :kind vector?))
(s/def ::keys ::ident-vec)
(s/def ::strs ::ident-vec)
(s/def ::syms ::ident-vec)

(s/def ::opts (s/keys :opt-un [::as ::or ::keys ::strs ::syms]))

に準拠するマップ::assocと に準拠するマップをマージして作成できるマップの仕様を作成するにはどうすればよいですか? 私はあることを知っています:::mappings::optsmerge

(s/def ::assoc (s/merge ::opts ::mappings))

しかし、これmergeは基本的に の類似物であるため、機能しませんand。に似たものを探していorますが、マップ用です。

4

2 に答える 2

5

s/mergeマップのs/keyss/everyをタプルとして使用して、ハイブリッド マップを指定できます。より簡単な例を次に示します。

(s/def ::a keyword?)
(s/def ::b string?)
(s/def ::m
  (s/merge (s/keys :opt-un [::a ::b])
           (s/every (s/or :int (s/tuple int? int?)
                          :option (s/tuple keyword? any?))
                    :into {})))

(s/valid? ::m {1 2, 3 4, :a :foo, :b "abc"}) ;; true

この単純な定式化には、配座異性体アプローチよりもいくつかの利点があります。最も重要なことは、真実を述べていることです。さらに、それ以上の労力をかけずに、生成、適合、および非形成を行う必要があります。

于 2016-08-11T20:12:04.033 に答える
1

s/conformer中間ステップとして使用してs/and、マップを検証しやすい形式に変換できます。

(s/def ::assoc
  (s/and
    map?
    (s/conformer #(array-map
                    ::mappings (dissoc % :as :or :keys :strs :syms)
                    ::opts     (select-keys % [:as :or :keys :strs :syms])))
    (s/keys :opt [::mappings ::opts])))

それは例えばからあなたを得るでしょう

{ key :key
  :as name }

{ ::mappings { key :key }
  ::opts     { :as name } }
于 2016-07-05T14:45:14.353 に答える