匿名“自我指涉”的数据结构的意见/讨论(Advice/discussion on anonymous

2019-07-30 14:47发布

对于任何错误的术语道歉 - 我很新的计算机科学,我几乎只知道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功能有一个标准的降低状累加器,它只是积聚形式的元件。 它也有一个蓄电池组,其变长,每次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是装过用面向对象的意思,我不知道,我基本上只熟悉使用Clojure。
  3. 我非常新的计算机科学,是否有相关的这种类型的东西访问和信息资源 - 我想我会称之为匿名的自我指涉(也许是反身一个更好的词?)数据结构? 我还没有发现任何东西都平易近人和信息呢。
  4. 有没有更好的方式来写的self-ish宏? 以上,我已经包括我目前它的版本,但我不能动摇的感觉有可能是一个更简单的方法。
  5. 我有什么可能是“最聪明”的实施细节的各种问题。

    • 穿越 :它应该是广度优先或深度第一? 如果深度优先,序,后序,或序? 现在,我相信这是深度优先序,这对我来说很有意义,但也许有一些缺点我都没有注意到。
    • 订单问题 :( 在这里看到我的相关前面的问题 )在{}即手写地图),这是不可能的,而无需使用正确维护秩序(高于8映射条目) array-mapsorted-map --in换句话说以上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的宏观背景之外,但是这可能永远是有用的?

感谢您的阅读,任何帮助表示赞赏:)

Answer 1:

这种方法“感觉”有点我错了,虽然我不明白为什么。 也许我不喜欢图谱的构建依赖于订单的想法....

如此说来这是一个非常简单的宏来写,你有效地想要的东西,扩展为:

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

因此,合适的微距会是这样的(在地图的情况下):

(defmacro self-ish [bindings]
  `(let [~'this {}
         ~@(mapcat 
           #(do `(~'this (assoc ~'this ~@%)) )    
           (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)})


Answer 2:

在此方案与实现(letrec ...)它可以让你指的是结构本身内部的数据结构的名称。 所以,如果你想定义自己这样做的方式,它可能会使实现一个更有意义。 你可以用与在答案中描述引用的招数做是否有可能建立在Clojure的循环引用?

letrec的一个优点是它有一个用户指定的名称(如果您的宏嵌套那么this被屏蔽)。

[编辑,以去除类型的评论,因为我不明白您的宏。]

[更新]也,你可能会在Clojure中第8.5.1节的喜悦兴趣照应的讨论



文章来源: Advice/discussion on anonymous “self referential” data structures