简体   繁体   English

Clojure规范的哈希图具有相互依赖的值?

[英]clojure spec for hash-map with interdependent values?

I want to write a clojure 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. 我想为哈希映射写一个clojure规范,其中一个键的值被约束为等于其他两个键的值之和。 I know one way to write a test generator for such a spec by hand: 我知道一种为这种规范手工编写测试生成器的方法:

(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} 

But I haven't figured out how to write a spec for it, let alone a spec that produces a similar generator. 但是我还没有弄清楚如何编写规范,更不用说产生类似生成器的规范了。 The gist of the problem is that I lack, in the space of specs, a way to get hold of the "preimage" in the spec, that is, I lack an analogue to bind from the space of generators. 问题的要点在于,在规范的范围内,我缺乏一种掌握规范中“原像”的方法,也就是说,我缺乏从生成器空间bind的类似物。 Here is a failed attempt: 这是一次失败的尝试:

(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} 

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

I'd be grateful for any guidance. 如有任何指导,我将不胜感激。

EDIT : Today I Learned about s/with-gen , which will allow me to attach my (working) test generator to my "preimage" or "partial" spec. 编辑 :今天我了解了s/with-gen ,这将使我可以将(工作的)测试生成器附加到我的“原像”或“部分”规范中。 Perhaps that's the best way forward? 也许那是最好的前进方向?

You could use the nat-int? 您可以使用nat-int? predicate (for which there's a built-in spec, thanks @glts) for the count keys, and add a ::total-counts spec too: 谓词(有一个内置的规范,感谢@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 哈希映射的规范,其中键之一的值被约束为等于其他两个键的值之和

To add this assertion you can s/and a predicate function with the keys spec (or in this example the merge spec that merges the partial map spec with a ::total-count keys spec): 要添加这种说法,您可以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))))

This also uses with-gen to associate a custom generator with the spec that sets ::total-count to the sum of the other count keys. 它还使用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})

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

I'd recommend against using specs to calculate/add ::total-counts to the map. 我建议不要使用规格来计算::total-counts到地图中。 Specs generally shouldn't be used for data transformation. 规范通常不应该用于数据转换。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM