首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Clojure:指定几个约束;被迫“突破”为谓词。有更好的方法吗?

Clojure:指定几个约束;被迫“突破”为谓词。有更好的方法吗?
EN

Stack Overflow用户
提问于 2019-09-06 12:38:23
回答 2查看 160关注 0票数 3

我有这样的结构,用正方形平铺二维平面:

  • “条”是具有相同x坐标的“正方形”的集合。
  • 表示条带的结构图将所述x坐标保存为::sq-x,在该x坐标处的方格映射为::squares
  • 正方形地图的键是正方形的y坐标。
  • 正方形地图的值是正方形。
  • 正方形是一个结构映射,其x坐标和y坐标分别为::sq-x::sq-y,以及“顶点”::vtxs的向量。

当然,正方形::sq-x和条形的::sq-x以及正方形::sq-y和映射键之间也有相等的约束。

由于我们没有在Clojure中声明这些结构,因此指定它们在某种程度上成为Java中的类/类型声明的主干。

如何规范基本结构是相当清楚的,但为了规范映射和两个约束,我必须“突破”为谓词check-squares-map

代码语言:javascript
运行
复制
(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规范的“规范性”,从而使扩展到谓词的可能性降到最低?特别是,不同结构映射的值之间的约束似乎很难表达。指定是“本地的”(或者说是本地的?)

EN

回答 2

Stack Overflow用户

发布于 2019-09-06 13:34:11

这个问题显示了使用类型捕捉数据中错误的局限性。

例如,Java中的类型可以很容易地区分int或float。它不如分离出int或float的有效值,例如奇数可以但偶数是坏的,或者只有范围[0..12)中的值。事实上,有些类型(如无理数)根本无法精确表示。

一个更好的方法(如果对您的问题可行的话)是重新组织数据结构,以避免可能的内部冲突(例如冲突的:sq-x值)。例如,您可以定义一个更像这样的“条子”:

代码语言:javascript
运行
复制
- 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)],以防您想访问特定方块的坐标。

票数 0
EN

Stack Overflow用户

发布于 2019-09-08 14:23:24

经过反思,我发现了这个。

还没有,因为一些测试通过了,应该会失败。BRB。

代码语言:javascript
运行
复制
(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上失败了。

采取错误的结构

代码语言:javascript
运行
复制
#:foo.bar{:sq-x 1, :squares {0 #:foo.bar{:sq-x 66, :sq-y 0, :vtxs []}}}

其中sq-x在一边是1,而在另一边是66。

不管用:

$ lein repl

代码语言:javascript
运行
复制
(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

艾伊。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57822199

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档