具有相互依赖值的哈希映射的 clojure 规范?
clojure spec for hash-map with interdependent values?
我想为散列映射编写一个 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}
但我还没有想出如何为它编写规范,更不用说规范了
产生一个类似的发电机。问题的要点是,在规范的 space 中,我缺少一种方法来
掌握规范中的 "preimage" ,也就是说,我缺少与 bind
类似的东西
来自 space 个生成器。这是一次失败的尝试:
(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
,这将使我能够附加
我的(工作中的)测试生成器到我的 "preimage" 或 "partial" 规范。也许
这是最好的前进方式吗?
您可以使用 nat-int?
谓词(为此有一个 built-in 规范,感谢@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]))
spec for a hash-map wherein the value of one of the keys is constrained to be equal to the sum of the values of two other keys
要添加此断言,您可以 s/and
具有 keys
规范的谓词函数(或者在此示例中 merge
规范将部分映射规范与 ::total-count
按键规格):
(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})
The generated sample conforms to the spec because #(into % {...})
is truthy, but the result doesn't contain the new attribute with the key ::total-counts
.
我建议不要将规格 calculate/add ::total-counts
用于地图。规范通常不应用于数据转换。
我想为散列映射编写一个 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}
但我还没有想出如何为它编写规范,更不用说规范了
产生一个类似的发电机。问题的要点是,在规范的 space 中,我缺少一种方法来
掌握规范中的 "preimage" ,也就是说,我缺少与 bind
类似的东西
来自 space 个生成器。这是一次失败的尝试:
(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
,这将使我能够附加
我的(工作中的)测试生成器到我的 "preimage" 或 "partial" 规范。也许
这是最好的前进方式吗?
您可以使用 nat-int?
谓词(为此有一个 built-in 规范,感谢@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]))
spec for a hash-map wherein the value of one of the keys is constrained to be equal to the sum of the values of two other keys
要添加此断言,您可以 s/and
具有 keys
规范的谓词函数(或者在此示例中 merge
规范将部分映射规范与 ::total-count
按键规格):
(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})
The generated sample conforms to the spec because
#(into % {...})
is truthy, but the result doesn't contain the new attribute with the key::total-counts
.
我建议不要将规格 calculate/add ::total-counts
用于地图。规范通常不应用于数据转换。