2012-07-25 60 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