clojure-spec:无法正确获得函数的后置条件
clojure-spec: Unable to get function's postcondition right
我正在尝试 clojure spec 一个简单的函数,该函数计算方阵中 (row,col) 位置的 "neighbours"。例如,对于下面给出的 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”中找到;但是这个代码被修改了,因为原来的代码对我来说太紧凑了。然后我试着规范它并检查: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")
您只是缺少 #
前缀以使您的匿名函数处于 :post
条件。 post 条件需要是一个函数,可以获取主题函数调用的输出。
:post [#(s/valid? ::be-row-col-vector %)]
也可以改写为:
:post [(fn [o] (s/valid? ::be-row-col-vector o))]
但根据您的使用情况,您可能需要查看 function specs and instrument
as an alternative to :pre
and :post
conditions. I wrote more examples here.
我正在尝试 clojure spec 一个简单的函数,该函数计算方阵中 (row,col) 位置的 "neighbours"。例如,对于下面给出的 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”中找到;但是这个代码被修改了,因为原来的代码对我来说太紧凑了。然后我试着规范它并检查: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")
您只是缺少 #
前缀以使您的匿名函数处于 :post
条件。 post 条件需要是一个函数,可以获取主题函数调用的输出。
:post [#(s/valid? ::be-row-col-vector %)]
也可以改写为:
:post [(fn [o] (s/valid? ::be-row-col-vector o))]
但根据您的使用情况,您可能需要查看 function specs and instrument
as an alternative to :pre
and :post
conditions. I wrote more examples here.