【问题标题】:clojure spec for hash-map with interdependent values?具有相互依赖值的哈希映射的clojure规范?
【发布时间】:2019-11-29 10:43:20
【问题描述】:

我想为哈希映射编写一个 clojure 规范,其中一个值 键被限制为等于其他两个键的值之和。一世 知道一种手动为此类规范编写测试生成器的方法:

(ns my-domain)
(require '[clojure.test           :refer :all     ]
         '[clojure.spec.alpha     :as s           ]
         '[clojure.spec.gen.alpha :as gen         ]
         '[clojure.pprint         :refer (pprint) ])

(s/def ::station-id string?)
(s/def ::sim-time (s/double-in :infinite? true, :NaN? false))
(s/def ::reserved-counts (s/and int? #(not (neg? %))))
(s/def ::free-counts     (s/and int? #(not (neg? %))))

(def counts-preimage (s/gen (s/keys :req [::station-id
                                          ::sim-time
                                          ::reserved-counts
                                          ::free-counts])))

(pprint (gen/generate
         (gen/bind
          counts-preimage
          #(gen/return
            (into % {::total-counts
                     (+ (::reserved-counts %)
                        (::free-counts %))})))))
#:my-domain{:station-id "sHN8Ce0tKWSdXmRd4e46fB",
            :sim-time -3.4619293212890625,
            :reserved-counts 58,
            :free-counts 194,
            :total-counts 252}

但是我还没有想出如何为它写一个规范,更不用说一个规范了 产生一个类似的生成器。问题的要点是,在规格空间中,我缺乏一种方法来 掌握规范中的“原像”,也就是说,我缺少 bind 的类似物 从发电机的空间。这是一次失败的尝试:

(s/def ::counts-partial-hash-map
  (s/keys :req [::station-id
                ::sim-time
                ::reserved-counts
                ::free-counts]))
(s/def ::counts-attempted-hash-map
  (s/and ::counts-partial-hash-map
         #(into % {::total-counts (+ (::reserved-counts %)
                                     (::free-counts %))})))

(pprint (gen/generate (s/gen ::counts-attempted-hash-map)))
#:my-domain{:station-id "ls5qBUoF",
            :sim-time ##Inf,
            :reserved-counts 56797960,
            :free-counts 17}

生成的样本符合规范,因为#(into % {...}) 是真实的, 但结果不包含键为 ::total-counts 的新属性。

如果您有任何指导,我将不胜感激。

编辑:今天我了解了s/with-gen,这将允许我附加 我的(工作)测试生成器到我的“原像”或“部分”规范。可能 这是最好的前进方式吗?

【问题讨论】:

    标签: clojure clojure.spec


    【解决方案1】:

    您可以使用nat-int? 谓词(有一个内置规范,感谢@glts)作为计数键,并添加一个::total-counts 规范:

    (s/def ::reserved-counts nat-int?)
    (s/def ::free-counts nat-int?)
    (s/def ::total-counts nat-int?)
    
    (s/def ::counts-partial-hash-map
      (s/keys :req [::station-id
                    ::sim-time
                    ::reserved-counts
                    ::free-counts]))
    

    哈希映射规范,其中一个键的值被限制为等于其他两个键的值之和

    要添加此断言,您可以使用 keys 规范 s/and 一个谓词函数(或者在本例中,将部分映射规范与 ::total-count 键规范合并的 merge 规范):

    (s/def ::counts-attempted-hash-map
      (s/with-gen
        ;; keys spec + sum-check predicate
        (s/and
          (s/merge ::counts-partial-hash-map (s/keys :req [::total-counts]))
          #(= (::total-counts %) (+ (::reserved-counts %) (::free-counts %))))
        ;; custom generator
        #(gen/fmap
           (fn [m]
             (assoc m ::total-counts (+ (::reserved-counts m) (::free-counts m))))
           (s/gen ::counts-partial-hash-map))))
    

    这还使用with-gen 将自定义生成器与将::total-count 设置为其他计数键之和的规范相关联。

    (gen/sample (s/gen ::counts-attempted-hash-map) 1)
    => (#:user{:station-id "", :sim-time 0.5, :reserved-counts 1, :free-counts 1, :total-counts 2})
    

    生成的样本符合规范,因为#(into % {...}) 是真实的,但结果不包含键为::total-counts 的新属性。

    我建议不要使用规范来计算/添加 ::total-counts 到地图。规范通常不应用于数据转换。

    【讨论】:

    • 对我来说打开理解大门的钥匙是s/merge。我不知道,谢谢。
    • 顺便说一下,我相信nat-int? 等同于您的::non-neg-int 规范。
    猜你喜欢
    • 1970-01-01
    • 2018-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-18
    • 2017-09-13
    • 1970-01-01
    相关资源
    最近更新 更多