我正在使用clojure.spec来验证地图条目的向量。该向量看起来如下:
[{:point {:x 30 :y 30}}
{:point {:x 34 :y 33}}
{:user "joe"}]我想构造规范以要求1.N个::point条目,并且只需要一个::user条目。
以下是我(不成功)构建此规范的尝试:
(s/def ::coord (s/and number? #(>= % 0)))
(s/def ::x ::coord)
(s/def ::y ::coord)
(s/def ::point (s/keys :req-un [::x ::y]))
(s/def ::user (s/and string? seq))
(s/def ::vector-entry (s/or ::pt ::user))
(s/def ::my-vector (s/coll-of ::vector-entry :kind vector))当我只运行一个::point条目的验证时,它可以工作:
spec> (s/valid? ::point {:point {:x 0 :y 0}})
true
spec> (s/valid? ::my-vector [{:point {:x 0 :y 0}}])
false对于如何构造s/or部件,以便向量条目可以是::user或::point类型,有什么想法吗?
另外,对于如何在向量中要求一个和唯一一个::user条目和1.N个::point条目,有什么想法吗?
发布于 2017-06-17 14:36:12
以下是您问题中数据的可能规范:
(require '[clojure.spec.alpha :as s])
(s/def ::coord nat-int?)
(s/def ::x ::coord)
(s/def ::y ::coord)
(s/def ::xy (s/keys :req-un [::x ::y]))
(s/def ::point (s/map-of #{:point} ::xy))
(s/def ::username (s/and string? seq))
(s/def ::user (s/map-of #{:user} ::username))
(s/def ::vector-entry (s/or :point ::point :user ::user))
(s/def ::my-vector (s/coll-of ::vector-entry :kind vector))
(s/valid? ::point {:point {:x 0 :y 0}})
(s/valid? ::my-vector [{:point {:x 0 :y 0}}])
(s/valid? ::my-vector [{:point {:x 0 :y 0}} {:user "joe"}])几点意见:
or规范要求指定规范的名称。:point或:user类型标记不同的项目需要一定程度的间接,我在顶部使用map-of,在嵌套级别使用keys,但有很多选择。:user是必需的时,为什么强迫程序执行O(N)搜索呢?希望这能有所帮助!
发布于 2017-06-17 20:02:57
虽然Stuart的回答很有教育意义,并且解决了您的许多问题,但我不认为它包含了确保“一个和唯一一个::user条目”的标准。
剽窃他的答案:
(s/def ::coord nat-int?)
(s/def ::x ::coord)
(s/def ::y ::coord)
(s/def ::xy (s/keys :req-un [::x ::y]))
(s/def ::point (s/map-of #{:point} ::xy))
(s/def ::username (s/and string? seq))
(s/def ::user (s/map-of #{:user} ::username))
(s/def ::vector-entry (s/or :point ::point
:user ::user))
(s/def ::my-vector (s/and (s/coll-of ::vector-entry
:kind vector)
(fn [entries]
(= 1
(count (filter (comp #{:user}
key)
entries))))))
(s/valid? ::point {:point {:x 0 :y 0}})
;; => true
(s/valid? ::my-vector [{:point {:x 0 :y 0}}])
;; => false
(s/valid? ::my-vector [{:point {:x 0 :y 0}}
{:user "joe"}])
;; => true
(s/valid? ::my-vector [{:point {:x 0 :y 0}}
{:point {:x 1 :y 1}}
{:user "joe"}])
;; => true
(s/valid? ::my-vector [{:point {:x 0 :y 0}}
{:user "joe"}
{:user "frank"}])
;; => false重要的补充是在::my-vector的规范中。注意,s/or的一致输出是一个映射项,这就是传递给新的自定义谓词的内容。
我应该指出,虽然这是可行的,但它为您的验证增加了另一个线性扫描。不幸的是,我不知道规范是否能在一次传递中提供一种很好的方法。
发布于 2017-06-21 08:42:46
蒂姆和斯图尔特的回答解决了这个问题,并提供了非常丰富的信息。我想指出的是,Clojure规范还有一个特性,可以用来为点和用户的向量指定结构。
也就是说,规范允许使用正则表达式来指定序列。有关更多信息,请参见规范指南。
下面是对序列使用规范的解决方案。这建立在以前的解决方案的基础上。
(require '[clojure.spec.alpha :as s])
(s/def ::coord nat-int?)
(s/def ::x ::coord)
(s/def ::y ::coord)
(s/def ::xy (s/keys :req-un [::x ::y]))
(s/def ::point (s/map-of #{:point} ::xy))
(s/def ::username (s/and string? seq))
(s/def ::user (s/map-of #{:user} ::username))
(s/def ::my-vector (s/cat :points-before (s/* ::point)
:user ::user
:points-after (s/* ::point)))
(s/valid? ::point {:point {:x 0 :y 0}})
;; => true
(s/valid? ::my-vector [{:point {:x 0 :y 0}}])
;; => false
(s/valid? ::my-vector [{:point {:x 0 :y 0}}
{:user "joe"}])
;; => true
(s/valid? ::my-vector [{:point {:x 0 :y 0}}
{:point {:x 1 :y 1}}
{:user "joe"}])
;; => true
(s/valid? ::my-vector [{:point {:x 0 :y 0}}
{:user "joe"}
{:user "frank"}])
;; => false
(s/valid? ::my-vector [{:point {:x 0 :y 0}}
{:user "joe"}
{:point {:x 1 :y 1}}])
;; => true如果在最后需要::user条目,则可以很容易地适应这种情况。
https://stackoverflow.com/questions/44605314
复制相似问题