我如何指定混合地图?
How can I spec a hybrid map?
写入后, I was inspired to try to specify Clojure's destructuring language using 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]))
如何为可以通过将符合 ::mappings
的地图和符合 ::opts
的地图合并在一起创建的地图创建 ::assoc
规范?我知道有 merge
:
(s/def ::assoc (s/merge ::opts ::mappings))
但这行不通,因为 merge
基本上是 and
. I'm looking for something that's analogous to or
的类似物,但用于地图。
您可以使用 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 } }
您可以使用 s/keys
的 s/merge
和地图的 s/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
与构象异构方法相比,这种更简单的公式有几个好处。最重要的是,它陈述了事实。此外,它应该无需进一步努力即可生成、符合和取消格式。
写入后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]))
如何为可以通过将符合 ::mappings
的地图和符合 ::opts
的地图合并在一起创建的地图创建 ::assoc
规范?我知道有 merge
:
(s/def ::assoc (s/merge ::opts ::mappings))
但这行不通,因为 merge
基本上是 and
. I'm looking for something that's analogous to or
的类似物,但用于地图。
您可以使用 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 } }
您可以使用 s/keys
的 s/merge
和地图的 s/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
与构象异构方法相比,这种更简单的公式有几个好处。最重要的是,它陈述了事实。此外,它应该无需进一步努力即可生成、符合和取消格式。