【问题标题】:How can I spec a hybrid map?如何指定混合地图?
【发布时间】:2023-03-31 08:43:01
【问题描述】:

写完this answer后,我受到启发尝试使用spec指定Clojure's destructuring language

(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 的类似物。我正在寻找类似于 or 的东西,但用于地图。

【问题讨论】:

    标签: clojure destructuring clojure.spec


    【解决方案1】:

    您可以使用映射的s/merges/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
    

    与构象方法相比,这种更简单的公式有几个好处。最重要的是,它陈述了事实。此外,它应该无需进一步努力即可生成、符合和取消变形。

    【讨论】:

      【解决方案2】:

      您可以使用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 } }
      

      【讨论】:

      • 谢谢!我确信有这样的东西可以使用。不过,它看起来很丑;希望以后能加入更优雅的解决方案。
      猜你喜欢
      • 2023-03-16
      • 2011-01-05
      • 1970-01-01
      • 2023-03-29
      • 1970-01-01
      • 2015-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多