2017-02-15 42 views
6

我還沒有找到任何如何執行遞歸實體規範的示例,如我在下面嘗試。我意識到::left::right由於它們尚未定義而失敗,所以我想知道如何在::node規範中遞歸地定義它們。遞歸實體規範

(s/def ::key string?) 
(s/def ::value string?) 
(s/def ::left ::node) 
(s/def ::right ::node) 
(s/def ::n int?) 
(s/def ::node (s/keys :req [::key ::value ::n] 
         :opt [::left ::right])) 

(defn test-it [] 
    (s/valid? ::node 
      {::key "hi" 
      ::value "bye" 
      ::n 0 
      ::left {::key "what" 
        ::value "nothing" 
        ::n 0} 
      ::right {::key "hello" 
         ::value "goodbye" 
         ::n 0} 
      })) 
+0

它看起來對我來說,它的抱怨':: node'未在規定':: left'和':: right'的定義,所以你可能想試試在這兩個之前定義':: node'。 –

+1

@Sam Estep:那麼你會遇到同樣的問題,因爲':: node'是用':: left'和':: right'來定義的,它還沒有被定義。需要斷路器,如Clojure中的'declare'。 –

+1

@ChrisMurphy不,它在':: node'後定義的':: left'和':: right'可以正常工作。查看我的答案,查看粘貼的REPL會話。 –

回答

1

你有沒有留下什麼,右的實體,而是以相同的方式定義兩個節點,不幸的是,你不能在地圖上同名的兩個鍵,因爲規範不允許「走樣「的關鍵字,而是使用關鍵字本身來識別規範。

如果您願意,您可以根據單個::children密鑰來定義左右節點,該密鑰是(一個或兩個)::node的集合。

(s/def ::key string?) 
(s/def ::value string?) 
(s/def ::n int?) 

(s/def ::node (s/keys :req [::key ::value ::n])) 
(s/def ::children (s/coll-of ::node :count 2)) 
;; for 1 or 2 children: (s/coll-of ::node :min-count 1 :max-count 2) 

(s/valid? ::node 
    {::key "parent-1" ::value "parent-1" ::n 1 
    ::children [{::key "leaf-1" ::value "leaf-1" ::n 2} 
       {::key "parent-2" ::value "parent-2" ::n 3 
       ::children [{::key "leaf-2" ::value "leaf-2" ::n 4} 
          {::key "leaf-3" ::value "leaf-3" ::n 5}]}]}) 

這給你一個類似的結構,與含有兩個節點,而不是兩個密鑰,每一個節點向量的輕微額外的複雜性。

另外,允許定義純粹的自身條件選擇是放棄一個地圖結構,並做了嵌套向量來代替:

(s/def ::node (s/or :parent (s/coll-of ::node :count 2) 
        :leaf (s/tuple ::key ::value ::n))) 

(s/valid? ::node 
    [[[["a" "a" 1] 
    ["b" "b" 2]] 
    ["c" "c" 3]] 
    ["d" "d" 4]]) 

這工作,因爲該元素是連續的,不需要與相關一個唯一的關鍵,就像在上面的地圖結構中一樣(是的,向量也是關聯的,但是在這種情況下它們的順序特性被使用了)。這當然不是「乾淨」,第一種方法可能是首選,但如果您願意放棄關聯結構並將其交易爲順序結構,則這是一種選擇。

2

如果您將::left::right的定義移動到::node以下(如Sam Estep對該問題的評論所建議的那樣)在s/keys引用不會緊跟:

Clojure 1.9.0-alpha14 
user=> (require '[clojure.spec :as s]) 
nil 
user=> (s/def ::key string?) 
:user/key 
user=> (s/def ::value string?) 
:user/value 
user=> (s/def ::n int?) 
:user/n 
(s/def ::node (s/keys :req [::key ::value ::n] 
         :opt [::left ::right])) 
:user/node 
user=> (s/def ::left ::node) 
:user/left 
user=> (s/def ::right ::node) 
:user/right 
(defn test-it [] 
    (s/valid? ::node 
      {::key "hi" 
      ::value "bye" 
      ::n 0 
      ::left {::key "what" 
        ::value "nothing" 
        ::n 0} 
      ::right {::key "hello" 
         ::value "goodbye" 
         ::n 0} 
      })) 
#'user/test-it 
user=> (test-it) 
true 
user=> (s/valid? ::node {::key "hi" ::value "bye" ::n 0 ::left {}}) 
false 
2

不同於常規def S,s/def s爲不依賴聲明......的順序,當你的別名(s/def ::a ::b)其中::b必須::a之前定義除。

所以,要麼你重新排序s/def S按米哈爾的建議,或者你在包裝的(s/and)右手值:

(s/def ::key string?) 
(s/def ::value string?) 
(s/def ::left (s/and ::node)) 
(s/def ::right (s/and ::node)) 
(s/def ::n int?) 
(s/def ::node (s/keys :req [::key ::value ::n] 
         :opt [::left ::right])) 

(defn test-it [] 
    (s/valid? ::node 
      {::key "hi" 
      ::value "bye" 
      ::n 0 
      ::left {::key "what" 
        ::value "nothing" 
        ::n 0} 
      ::right {::key "hello" 
         ::value "goodbye" 
         ::n 0} 
      }))