2012-07-25 68 views
8

任何錯誤的術語道歉 - 我對計算機科學相當陌生,我幾乎只知道Clojure(但我想我會說我知道它很不錯)。關於匿名「自引用」數據結構的建議/討論

所以,我還沒有做過大量的研究,但是我有時會發現它在編寫Clojure代碼時能夠引用某些「我所處的任何數據結構的中間版本」在那個數據結構內(很像在let)。簡單的例子:

=> (self-ish {:a 10 
       :b (inc (this :a)) 
       :c (count (vals this))}) 
=> {:a 10, :b 11, :c 3} 
=> (self-ish ["a" "b" (reduce str this)]) 
=> ["a" "b" "ab"] 
//Works in any nested bits too 
=> (self-ish [1 2 3 [4 5 (first this)] 6 [7 [8 (cons (second this) (nth this 3))]]]) 
=> [1 2 3 [4 5 1] 6 [7 [8 (2 4 5 1)]]] 

的想法是,所述結構自身建立向上遞增,並在任何階段必須參考當前中間結構this的能力。下面是我當前的實現代碼:

//Random straightforward but helpful definitions 
(defn map-entry? [obj] 
    (instance? clojure.lang.AMapEntry obj)) 
(def Map clojure.lang.IPersistentMap) 
(def Vector clojure.lang.IPersistentVector) 
(def List clojure.lang.IPersistentList) 
(def Set clojure.lang.IPersistentSet) 

(defn append 
    [x coll] 
    (if-not coll x 
    (condp instance? coll 
     Map (if (empty? x) coll 
      (assoc coll (first x) (second x))) 
     Vector (conj coll x) 
     Set (conj coll x) 
     List (apply list (concat coll [x])) 
     (concat coll [x])))) 

(defn build-this 
    [acc-stack acc] 
    (->> (cons acc acc-stack) 
     (drop-while list?) 
     (drop-while (every-pred empty? identity)) 
     (reduce append))) 

(defn self-indulge 
    [acc-stack acc form] 
    ;//Un-comment the following to see it print intermediate stages of processing 
    #_(println "this:" (build-this acc-stack acc) "\n at:" form) 
    (append (cond 
      (coll? form) (reduce (partial self-indulge (cons acc acc-stack)) 
           (if (map-entry? form) [] 
            (empty form)) 
           form) 
      (= (quote this) form) (build-this acc-stack acc) 
      :else form) 
      acc)) 

(defmacro self-ish 
    [form] 
    (self-indulge() nil form)) 

append功能追加項目到集合,並返回相同類型的集合。 self-indulge函數有一個標準的類似reduce的累加器,它只是構建表單的元素。它也有一個累加器堆棧,每次self-indulge都會重複出現。關鍵是要跟蹤其他「更高」的累加器,這樣this將是整個結構,而不僅僅是一個本地塊。 self-ish宏只是很好地包裝self-indulge(它自稱使用partial,所以它不能穿宏褲)。

編輯:示例用例 對我來說,這個宏是關於試圖增加代碼的可讀性,而不是真正擴展功能。在我發現這種方法有用的情況下,我的手寫結構部分是冗餘數據 - 或者「依賴」是一個更好的詞。閱讀代碼並查看數據結構的哪些部分可以更容易,並且如果我在結構的一部分中修改數據值並希望該更改反映在其他部分中,它也可能很有用。例如:

=> (self-ish {:favorite-books (list "Crime and Punishment" "Mrs. Dalloway") 
       :favorite-things (list* "Ice Cream" "Hammocks" (this :favorite-books)}) 
=> {:favorite-things ("Ice Cream" "Hammocks" "Crime and Punishment" "Mrs. Dalloway"), 
    :favorite-books ("Crime and Punishment" "Mrs. Dalloway")} 

它也可能是有用的時候,其中一個可能會很喜歡的東西包括烤成的數據,而不是衍生有關使用某些功能的飛行。這些情況可能非常罕見,而且我認爲,當您可以使用乾淨的函數來操作數據時,不必要地糾結數據是一個糟糕的主意。

我的主要問題:

  1. 這是真正有用的,不然會含糊/複雜性招致太多?我想我不是一個人想要/使用這種類型的宏。這裏有什麼別人的經歷?你用這樣的東西嗎?你有沒有找到更好的解決方法?有沒有這樣的理由是不是在任何Clojure圖書館?還是有什麼我還沒有看到?
  2. 是否有更好的命名約定我可以使用 - 而不是self-ishthis?例如,也許this太OOP意義,我不確定,我基本上只熟悉Clojure。
  3. 我對計算機科學相當陌生,有沒有與這種類型的東西有關的可訪問和信息資源 - 我想我會稱之爲匿名的自引用(也許反身是一個更好的詞?)數據結構?我還沒有發現任何既平易近人又內容豐富的東西。
  4. 有沒有更好的方法來編寫self-ish宏?上面,我已經包含了我目前的版本,但我不能動搖這種感覺,可能有一種更簡單的方法。
  5. 我對什麼可能是「最明智的」實現細節有各種疑問。

    • Traversal:它應該先寬度還是先深度?如果先深入,預購,後序或中序?現在,我認爲這是深度優先,這對我來說很有意義,但也許它有一些我沒有注意到的缺點。
    • 秩序問題:(See here for a related previous question of mine)在{}這是不可能的,而無需使用array-mapsorted-map --in換句話說正常維持秩序(高於8映射條目),上述8個映射條目(即手寫地圖),{}用法不安全。也許而不是手寫的順序,宏可以做一些奇特的魔法來處理某些「理想」順序的項目?或者,將(array-map ...)內的所有地圖包裹起來,而不是令人愉快的{}

      //Showing maps with 9 entries failing 
      => (self-ish {:a 1 
             :b (inc (this :a)) 
             :c (inc (this :b)) 
             :d (inc (this :c)) 
             :e (inc (this :d)) 
             :f (inc (this :e)) 
             :g (inc (this :f)) 
             :h (inc (this :g)) 
             :i (inc (this :h))}) 
      => NullPointerException clojure.lang.Numbers.ops (Numbers.java:942) 
      //8 works fine 
      => (self-ish {:a 1 
             :b (inc (this :a)) 
             :c (inc (this :b)) 
             :d (inc (this :c)) 
             :e (inc (this :d)) 
             :f (inc (this :e)) 
             :g (inc (this :f)) 
             :h (inc (this :g))}) 
      => {:h 8, :g 7, :f 6, :e 5, :d 4, :c 3, :b 2, :a 1} 
      
    • 串行:正如我寫它,宏通過處理其元素串聯,類似let避免無限遞歸,但也不產生潛在的古怪行爲。例如,在我上面的快速示例中,(reduce str this)返回"ab",因爲this["a" "b"]在該步驟。也許有時創建某種無限的懶序列會有用嗎?如果是這樣,那麼如何實施?

    • 地圖項:眼下,映射條目被當作載體,但由於如何this可以在任何中間步驟被調用,這是完全可能獲得從具有鍵nil值「尚未」被分配了一個值。這就是爲什麼在我的第一個快速示例中,:c最終映射到3 - 因爲在中間有一個nil對應於:c,並且計數也是如此。你認爲這可以解決嗎?
    • 非宏實用程序:在宏上下文之外使用self-indulge只是微不足道,但是這可能有用嗎?

感謝您的閱讀,任何幫助表示讚賞:)在方案

+0

看起來很有趣給出有意義的名字,但你可以提比使事情複雜和模棱兩可的其他這樣的事情任何有用的用例。 – Ankur 2012-07-25 16:22:14

+0

看起來很哲學,也許這是錯誤的StackExchange網站來問這個問題。 – sortega 2012-07-25 16:57:40

+0

@Ankur好主意,我會編輯包含一個用例。這並不是說它擴展了任何功能,它可能使編寫代碼更直接/模塊化。老實說,我不確定這是否真的值得添加模糊性或複雜性,正如你指出的那樣。那正是我想要解決我的第一個問題,我也應該編輯它。 – 2012-07-25 20:17:48

回答

2

這種方法對我來說「感覺」有點不對,雖然我不太清楚爲什麼。也許我不喜歡圖譜的構建依賴於訂單的想法....

已經說過,這是一個很簡單的宏來寫,你有效地想要的東西,擴展爲:

(let [this {} 
     this (assoc this :a 1) 
     this (assoc this :b (+ (this :a) 3))] 
    this) 

因此合適的微距會是這樣的(在地圖的情況下):

(defmacro self-ish [bindings] 
    `(let [~'this {} 
     [email protected](mapcat 
      #(do `(~'this (assoc ~'this [email protected]%)))  
      (partition 2 bindings))] 
    ~'this)) 

(self-ish [:a 1 
      :b (+ (this :a) 3)]) 
=> {:b 4, :a 1} 

請注意,我做出的綁定形式的載體作爲地圖結合的形式是無序的。

還不知道我有多喜歡這個成語。我的首選方法通常是定義使用let形式的結構和臨時計算例如爲:

(let [meaningful-foo (something) 
     meaningful-bar (something-else)] 
    {:foo meaningful-foo 
    :bar meaningful-bar 
    :baz (some-calculation meaningful-foo meaningful-bar)}) 
+1

太棒了!底部的「let」完全是一個好主意,非常簡單 - 我從來沒有想過出於某種原因!在某些情況下,它會更加冗長,但可能幾乎總是比我的「自我」方法更簡單更清晰。至於你自己的「自我」宏的版本,就目前而言,除了只處理地圖,它也不適用於嵌套數據。最終,你可以讓宏觀擴展爲一箇中間的'this's,其中這些中介可能是一些'自我放縱'的「減少」,但我沒有看到它的優勢。 – 2012-07-26 04:06:07

2

這與(letrec ...),它可以讓你指的是結構本身內部的數據結構的名稱來完成。所以如果你想要定義你自己的做法,實現它可能更有意義。你可以做它用與在答案中描述引用技巧,letrec的Is it possible to create circular references in Clojure?

一個優點是它有一個用戶指定的名稱(如果您的宏嵌套然後this被遮擋)。

[編輯,以去除類型的註釋,因爲我不明白您的宏。]

[更新]也,你可能有興趣在照應的討論Clojure中的部分的喜悅8.5.1

+0

感謝您指出'letrec',我曾經看到過某處被遺忘 - 似乎超級相關。用我的代碼來做類似'letrec'的東西似乎很簡單。也許我不完全理解,但我不確定我是否同意那些循環參考示例會更好,如果僅僅因爲它添加了不必要的原子。是的,我的代碼對某些類型進行了測試,但只是爲了維護所有嵌套數據結構的輸入 - 目前,即使我使用了類似循環引用的東西,我也沒有看到解決方法。 – 2012-07-26 00:24:50

+0

如果我理解正確,原子在最後丟棄。所以在該鏈接的第二個示例中,最終使用'@ a'。我不明白你的宏觀tbh。 – 2012-07-26 01:31:22

+0

哦,這是因爲讀者正在創造我認爲的文字?好。在這種情況下,你也應該設置。嗯。不,不知道。抱歉。 – 2012-07-26 01:35:01