【问题标题】:clojure-spec: Unable to get function's postcondition rightclojure-spec:无法正确获取函数的后置条件
【发布时间】:2019-12-11 22:12:25
【问题描述】:

我正在尝试 clojure spec 一个简单的函数,该函数计算方阵中 (row,col) 位置的“邻居”。例如,对于下面给出的 4x4 矩阵,单元格 (1,1) 的邻居应为:(0,1)、(1,0)、(1,2)、(2,1)。单元格(4,3)的邻居,甚至不在矩阵的范围内,应该是(3,3)等。

函数的输入是矩阵的大小和感兴趣位置的 (row,col)。输出是邻居 (row,col) 的集合。如果没有邻居,这个集合可以是空的。

这个问题可以在“The Joy of Clojure, 2nd editions, page 94; but this code is modified because the original is too compact for me.然后我尝试规范它并检查:pre中的规范和:post 部分。

但是,我无法使用 :post 部分。当我运行测试用例时,我得到:

java.lang.ClassCastException: java.lang.Boolean cannot be cast
to clojure.lang.IFn

要改变什么?

(require '[clojure.spec.alpha :as s]
         '[clojure.test       :as t])

; ===
; Specs
; ===

(s/def ::be-row-col
       (s/coll-of integer? :count 2 :kind sequential?))
(s/def ::be-square-matrix-size
       (s/and integer? #(<= 0 %)))
(s/def ::be-row-col-vector
       (s/and (s/coll-of ::be-row-col) (s/int-in-range? 0 5 #(count %))))

; ===
; Function of interest
; ===

(defn neighbors [sqmsz rc]

     {:pre  [(s/valid? ::be-row-col rc)
             (s/valid? ::be-square-matrix-size sqmsz)] 
      :post [(s/valid? ::be-row-col-vector %)]
     }

     (let [ cross             [[-1 0] [1 0] [0 -1] [0 1]]
            in-sq-matrix?     (fn [x]
                                  (and (<= 0 x) (< x sqmsz)))
            in-sq-matrix-rc?  (fn [rc]
                                  (every? in-sq-matrix? rc))
            add-two-rc        (fn [rc1 rc2]
                                  (vec (map + rc1 rc2)))
            get-rc-neighbors  (fn [rc]
                                  (map (partial add-two-rc rc) cross)) ]
        (filter in-sq-matrix-rc? (get-rc-neighbors rc))))

; ===
; Put a collection of [row col] into an expected form
; ===
; this is used to run the test code

(defn formify [rc-coll]
   (let [ cmp (fn [rc1 rc2]
                  (let [ [r1 c1] rc1
                         [r2 c2] rc2 ]
                     (cond (< r1 r2) -1  ; sort by row
                           (> r1 r2) +1
                           (< c1 c2) -1  ; then by column
                           (> c1 c2) +1
                           true       0))) ]
      (vec (sort cmp rc-coll))))

; ===
; Testing
; ===

(defn test-nb [ sqmsz rc expected txt ]
   (do
      (t/is (= (formify (neighbors sqmsz rc)) expected) txt)
   ))

(test-nb  0  [0 0]  []  "Zero-size matrix, outside #1")    
(test-nb  0  [1 1]  []  "Zero-size matrix, outside #2")    
(test-nb  1  [0 0]  []  "One-size matrix, inside")    
(test-nb  1  [1 0]  [[0 0]]  "One-size matrix, outside")    
(test-nb  5  [0 0]  [[0 1] [1 0]]  "Testing top left")    
(test-nb  5  [1 0]  [[0 0] [1 1] [2 0]]  "Testing left edge")    
(test-nb  5  [1 1]  [[0 1] [1 0] [1 2] [2 1]]  "Testing middle #1")    
(test-nb  5  [2 2]  [[1 2] [2 1] [2 3] [3 2]]  "Testing middle #2")    
(test-nb  5  [3 3]  [[2 3] [3 2] [3 4] [4 3]]  "Testing middle #3")    
(test-nb  5  [4 4]  [[3 4] [4 3]]  "Testing btm right")    
(test-nb  5  [5 5]  []  "Testing outside #1")    
(test-nb  5  [5 4]  [[4 4]]  "Testing outside #2")    
(test-nb  5  [4 3]  [[3 3] [4 2] [4 4]]  "Testing btm edge")

【问题讨论】:

  • 经过进一步的编辑和实验,可以找到功能代码in this gist

标签: clojure clojure.spec


【解决方案1】:

您只是缺少 # 前缀来使您的匿名函数符合 :post 条件。后置条件必须是一个可以获取主题函数调用输出的函数。

:post [#(s/valid? ::be-row-col-vector %)]

也可以改写为:

:post [(fn [o] (s/valid? ::be-row-col-vector o))]

但根据您的用例,您可能需要研究 function specsinstrument 来替代 :pre:post 条件。我写了更多examples here.

【讨论】:

  • 太好了,谢谢。显然我的大脑还不习惯这种语法。功能规范将很快尝试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多