Omr*_*ein 8 macros clojure self-reference
对任何错误的术语表示歉意 - 我对计算机科学很陌生,我几乎只知道Clojure(但我想我会说我很清楚).
所以,我还没有对此做过大量的研究,但我有时发现在编写Clojure代码时能够从该数据结构中引用一些"我所处的任何数据结构的中间版本" (很像a 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)]]]
Run Code Online (Sandbox Code Playgroud)
这个想法是结构逐渐建立起来,并且在任何阶段都有能力将当前的中间结构称为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))
Run Code Online (Sandbox Code Playgroud)
该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")}
Run Code Online (Sandbox Code Playgroud)
它也可能在人们可能真的希望将某些内容包含在数据中时使用,而不是使用某些函数动态派生.这些情况可能非常罕见,我认为当你可以使用很好的清理函数来操作它时,不必要地纠缠数据是个坏主意.
我的主要问题:
self-ish和this?例如,也许this是OOP意义太大了,我不确定,我基本上只熟悉Clojure.self-ish宏?上面,我已经包含了我当前的版本,但我不能动摇可能有一种更简单的方式.关于什么可能是"最明智"的实施细节,我有各种各样的问题.
订单问题 :( 请参阅此处查看我之前的相关问题)内部{}(即手工绘制的地图)不能正确维护订单(超过8个地图条目)而不使用array-map或sorted-map- 换句话说,超过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}
Run Code Online (Sandbox Code Playgroud)序列:正如我所写的,宏通过串行处理其元素来避免无限递归,类似于let,但这确实产生了可能奇怪的行为.例如,在我上面的快速示例中,(reduce str this)返回"ab"因为this是["a" "b"]在该步骤.也许有时创建某种无限懒惰序列会有用吗?如果是这样,那将如何实施?
this可以在任何中间步骤调用,因此完全可以nil从"尚未"赋值的键中获取值.这就是为什么在我的第一个快速示例中,:c最终映射到3 - 因为中间有一个nil对应的:c,并且也被计算在内.你认为这需要修理吗?self-indulge在宏上下文之外使用它是微不足道的,但这可能有用吗?感谢阅读,任何帮助表示赞赏:)
这种方法对我来说“感觉”有点错误,尽管我不太清楚为什么。也许我不喜欢地图构建依赖于顺序的想法......
话虽如此,这是一个非常容易编写的宏,但您实际上想要扩展为:
(let [this {}
this (assoc this :a 1)
this (assoc this :b (+ (this :a) 3))]
this)
Run Code Online (Sandbox Code Playgroud)
因此,适当的宏类似于(对于地图情况):
(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}
Run Code Online (Sandbox Code Playgroud)
请注意,我将绑定形式设置为向量,因为地图绑定形式是无序的。
仍然不确定我有多喜欢这个习语。我首选的方法通常是使用 let 形式定义结构,并为临时计算提供有意义的名称,例如:
(let [meaningful-foo (something)
meaningful-bar (something-else)]
{:foo meaningful-foo
:bar meaningful-bar
:baz (some-calculation meaningful-foo meaningful-bar)})
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
379 次 |
| 最近记录: |