据我所知,walk和map都将函数应用于seq。 (walk也允许应用outer
函数后处理)。然而,使用一个而不是另一个的惯用案例是什么?
答案 0 :(得分:10)
map
的语义基本上是:将函数应用于集合中的每个项目,并按顺序返回结果:
(map inc #{0 1 2}) ;outputs (when realized) (1 2 3)
请注意,输入是一个集合,但输出是一个序列。
walk的语义基本上是:创建一个相同类型的集合,其中每个项目已被该项目的inner
函数的值替换,返回将outer
应用于新收藏:
(walk inc identity #{0 1 2}) ;outputs #{1 2 3}
如果你看一下walk API(http://richhickey.github.com/clojure/clojure.walk-api.html)中其他函数的源代码,你可以看到如何使walk也是递归的(或者只是使用其他函数)。
就成语而言,我不确定。但walk
更复杂,因此在您不需要map
提供的语义的情况下,您可能应该坚持使用walk
。
答案 1 :(得分:7)
将函数应用于seq是map的工作。当你必须遍历并递归地遍历整个结构时,请使用walk。
walk
的一些示例可以在ClojureDocs找到,也可以在REPL中找到,例如(user/clojuredocs clojure.walk/postwalk)
。许多示例都是教学法,可以而且应该在实践中使用map
或for
(有时是reduce
)。
walk
的典型用例是当您希望以递归方式处理嵌套结构时。可能有用的一些示例是clojure.walk
名称空间本身,例如看(source clojure.walk/keywordize-keys)
。 [注意,如果您想要迭代或随意处理它,请使用拉链(或tree-seq
用于一些更简单的迭代情况)。]
另一个想到的例子是解释解析树:
(require '[clojure.walk :as w])
(def t [+ [* [- 6 2] [/ 9 3]] [* 2 [+ 7 8]]])
(w/postwalk #(if (and (coll? %) (fn? (first %))) (apply (first %) (next %)) %) t)
;=> 42
如果将fn?
替换为allowed-fn?
等来评估edn表达式,而不是调用功能太强大的eval编译器,则可能很有用:
(eval t) ;=> [#<core$_PLUS_ ... ]
糟糕,表单是列表,而不是向量:
(def s (w/postwalk #(if (coll? %) (apply list %) %) t))
s ;=> (#<core$_PLUS_ ... )
(eval s) ;=> 42
啊,请注意另一个walk
的使用 - 递归地将结构从嵌套向量更改为嵌套列表。
冥想的反复例子:
(require '[clojure.walk :as w])
(def s1 (range 8))
s1 ;=> (0 1 2 3 4 5 6 7)
(map inc s1)
;=> (1 2 3 4 5 6 7 8)
(w/postwalk #(if (number? %) (inc %) %) s1)
;=> (1 2 3 4 5 6 7 8)
(def s2 (partition 2 s1))
s2 ;=> ((0 1) (2 3) (4 5) (6 7))
(map (partial map inc) s2)
;=> ((1 2) (3 4) (5 6) (7 8))
(w/postwalk #(if (number? %) (inc %) %) s2)
;=> ((1 2) (3 4) (5 6) (7 8))
(def s3 (partition 2 s2))
s3 ;=> ((0 1) (2 3) (4 5) (6 7))
(map (partial map (partial map inc)) s3)
;=> (((1 2) (3 4)) ((5 6) (7 8)))
(w/postwalk #(if (number? %) (inc %) %) s3)
;=> (((1 2) (3 4)) ((5 6) (7 8)))
(def s4 (partition 2 s3))
s4 ;=> ((((0 1) (2 3)) ((4 5) (6 7))))
(map (partial map (partial map (partial map inc))) s4)
;=> ((((1 2) (3 4)) ((5 6) (7 8))))
(w/postwalk #(if (number? %) (inc %) %) s4)
;=> ((((1 2) (3 4)) ((5 6) (7 8))))
答案 2 :(得分:0)
对于map
,我认为惯用例非常清楚:当您需要按顺序转换所有内容时,请使用它。
对于clojure.walk/walk
,我认为惯用例是:当您需要按顺序转换所有内容然后执行apply fn
操作时。
(clojure.walk/walk second #(apply + %) [["a" 1] ["b" 2] ["c" 3]])
;; => 6
(clojure.walk/walk first #(apply concat %) [["ab" 1] ["b" 2] ["c" 3]])
;; => (\a \b \b \c)
(mapcat first [["ab" 1] ["b" 2] ["c" 3]])
;; => (\a \b \b \c)
请注意clojure.walk/walk
和mapcat
之间的相似之处,我个人认为walk是mapcat的通用形式。