我有这样的结构,用正方形平铺二维平面:
::sq-x
,在该x坐标处的方格映射为::squares
。::sq-x
和::sq-y
,以及“顶点”::vtxs
的向量。当然,正方形::sq-x
和条形的::sq-x
以及正方形::sq-y
和映射键之间也有相等的约束。
由于我们没有在Clojure中声明这些结构,因此指定它们在某种程度上成为Java中的类/类型声明的主干。
如何规范基本结构是相当清楚的,但为了规范映射和两个约束,我必须“突破”为谓词check-squares-map
。
(ns foo.bar
(:require
[clojure.spec.alpha :as s]
[clojure.test :as t]))
(s/def ::sq-x integer?)
(s/def ::sq-y integer?)
(s/def ::vtxs sequential?)
(s/def ::square (s/keys :req [::sq-x ::sq-y ::vtxs]))
; ---
; Additional constraining of values
; ---
; Receive: the "strip", which is dezz'd ("destructured") into the "sq-x"
; master value and the "squares" map.
; What is done:
; - Transform the "squares" map into a lazy seq of booleans where "true"
; means constraints for that map entry passed.
; - Use every? on that seq for early return.
(defn- check-squares-map [{sq-x-master ::sq-x squares ::squares}]
(every?
(fn [key+val]
; dezz the pair key+val into: "sq-y-as-key" and a dezz'd "square"
(let [[ skey { sq-x ::sq-x sq-y ::sq-y vtxs ::vtxs } ] key+val ]
(and
(= sq-x sq-x-master)
(= skey sq-y))))
squares))
; ---
; spec-ing the "map of 'square' structs"
; ---
; We need a "map?" predicate because "s/every-kv" actually accepts
; '[]' as valid "associative collection".
; Note that s/every-kv will not necessarily check every "square" when
; called (it breaks off at some point)
(s/def ::squares
(s/and
map?
(s/every-kv ::sq-y ::square)))
; ---
; spec-ing the "strip" struct
; ---
; This spec constrains the "strip" struct.
; .. which transitively constrains the "squares" map.
; .... which transitively constrains the individual "square" structs in
; the "squares" map.
; But we need to enforce a "remote constraint" between a "square",
; the keys of the "squares" map the "strip". Which is done by calling the
; "check-squares-map" predicate. This is unsatisfying, as calling the predicate
; breaks good spec-reporting.
(s/def ::strip
(s/and
(s/keys :req [::sq-x ::squares])
#(check-squares-map %)))
请注意,规范::squares
不一定要检查每个正方形:每一个-kv。
“突围”是不幸的,因为那时s/explain
只会说“谓词失败”,但并不确切地说:
(s/explain ::sq-x 500::正方形{0 {:sq-x 66 ::sq-y 66 ::vtxs [] }})#:foo.bar{:sq 500,:正方形{0 #:foo.bar{:sq-x 66,:sq-y 66,:vtxs []}}-失败:(校验-平方-映射%)规范::foo.bar/条带
我们有一个失败,因为::sq-x
在“条形”上是500,而在“正方形”上是66。键在0和::sq-y
在66之间也有类似的不匹配。但这个信息是相当普遍的。
是否有一种编码方式或方法来修改上述内容,以增加::strip
规范的“规范性”,从而使扩展到谓词的可能性降到最低?特别是,不同结构映射的值之间的约束似乎很难表达。指定是“本地的”(或者说是本地的?)
发布于 2019-09-06 13:34:11
这个问题显示了使用类型捕捉数据中错误的局限性。
例如,Java中的类型可以很容易地区分int或float。它不如分离出int或float的有效值,例如奇数可以但偶数是坏的,或者只有范围[0..12)
中的值。事实上,有些类型(如无理数)根本无法精确表示。
一个更好的方法(如果对您的问题可行的话)是重新组织数据结构,以避免可能的内部冲突(例如冲突的:sq-x
值)。例如,您可以定义一个更像这样的“条子”:
- x ; the x value (lower-left corner)
- y-min ; the lowest y value (also LL corner)
- len ; y-max = y-min + len (inclusive or exclusive both work here)
- size ; the width/height of each square (if necessary)
使用上述方法,您还可以在需要时计算每个方块的顶点。您还可以将从最小到最大的方块编号为[0..(len-1)]
,以防您想访问特定方块的坐标。
发布于 2019-09-08 14:23:24
经过反思,我发现了这个。
还没有,因为一些测试通过了,应该会失败。BRB。
(ns foo.bar
(:require
[clojure.spec.alpha :as s]
[clojure.test :as t]))
(s/def ::sq-x integer?)
(s/def ::sq-y integer?)
(s/def ::vtxs sequential?)
(s/def ::square (s/keys :req [::sq-x ::sq-y ::vtxs]))
; ---
; spec-ing the "map of 'square' structs"
; ---
; We need a "map?" predicate because "s/every-kv" actually accepts
; '[]' as valid "associative collection".
; Note that s/every-kv will not necessarily check every "square" when
; called (it breaks off at some point)
(s/def ::squares
(s/and
map?
(s/every-kv ::sq-y ::square)))
; ---
; Is this right?
; ---
; Here we assemble a new spec to be used in ::strip).
; The assemble function takes two parameters and returns a spec,
; i.e. a function taking a "strip" struct.
; (Can I even return a spec function from inside a let? Should work,
; no impediment to this orthogonality that I can see)
(defn assemble-new-spec [sq-x-master squares]
(fn [strip]
(let [squares (get strip ::squares)]
((s/and
(s/every (fn [keey vaal] (= sq-x-master (get vaal ::sq-x))))
(s/every (fn [keey vaal] (= keey (get vaal ::sq-y)))))
strip)))) ; and here we pass "strip" to the s/and for evaluation
(s/def ::strip
(s/and
(s/keys :req [::sq-x ::squares])
#(assemble-new-spec (get % ::sq-x) (get % ::squares)))) ; hmm....
遗憾的是,这在REPL上失败了。
采取错误的结构
#:foo.bar{:sq-x 1, :squares {0 #:foo.bar{:sq-x 66, :sq-y 0, :vtxs []}}}
其中sq-x
在一边是1,而在另一边是66。
不管用:
$ lein repl
(require '[clojure.test :as t :refer [deftest testing is]]
'[clojure.spec.alpha :as s :refer [valid?]]
'[foo.bar :as sut])
(def borked {
::sut/sq-x 1
::sut/squares {0 { ::sut/sq-x 66 ::sut/sq-y 0 ::sut/vtxs [] }}})
(valid? ::sut/strip borked)
;=> true
艾伊。
https://stackoverflow.com/questions/57822199
复制相似问题