[英]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}
但是我还没有弄清楚如何编写规范,更不用说产生类似生成器的规范了。 问题的要点在于,在规范的范围内,我缺乏一种掌握规范中“原像”的方法,也就是说,我缺乏从生成器空间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
,这将使我可以将(工作的)测试生成器附加到我的“原像”或“部分”规范中。 也许那是最好的前进方向?
您可以使用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]))
哈希映射的规范,其中键之一的值被约束为等于其他两个键的值之和
要添加这种说法,您可以s/and
与谓语功能keys
规范(或本例中的merge
规范是合并了部分地图规范::total-count
键SPEC):
(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
设置为其他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
到地图中。 规范通常不应该用于数据转换。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.