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/mergeofs/keyss/everyof 映射作为元组来指定混合映射。这是一个更简单的例子:

(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 回答