如何在 Clojure 中递归展平任意嵌套的向量和映射?
Posted
技术标签:
【中文标题】如何在 Clojure 中递归展平任意嵌套的向量和映射?【英文标题】:how can I recursively flatten arbitrarily nested vectors and maps in Clojure? 【发布时间】:2016-09-11 17:09:20 【问题描述】:我正在尝试使用递归来遍历 Clojure 中任意嵌套的向量和映射树,并返回一个仅包含关键字的向量,包括顶部。
所以下面的示例数据应该返回:
[:top :top :top :top :top :top :top :top :bottom :bottom :bottom :bottom :bottom :bottom :bottom :bottom :bottom :bottom :bottom :bottom]
,
但没有特定的顺序。
有人可以帮我正确地做到这一点吗?以下是我目前所拥有的。
(def sample [:top :top :top [:bottom :top :top [:bottom :bottom :bottom] :bottom :bottom :bottom],
:top :top [:bottom :bottom :bottom],
:top [:bottom :bottom]])
(defn make-flat [graph]
(loop [graph graph]
(if (every? keyword? graph) graph
(recur (into graph (flatten (seq (first (filter #(not (keyword? %)) graph)))))))))
(make-flat sample)
【问题讨论】:
为什么一开始就有这样一个任意嵌套的序列?通常,只生成一开始就正确形成的数据,而不是对其应用这种奇怪的修复,这通常是可能的,而且更好。 这是一个编码挑战 :) 如果这是一个编码挑战,而我给了你答案,我是否因解决编码挑战而获得奖励? 我实际上已经解决了这个挑战,但我个人正在寻找一个更通用的解决方案来解决我上面概述的问题。我可以通过接受您的回答来表扬您。 【参考方案1】:我怀疑 clojure.clj = clojure 基础库中已经有一个函数可以执行此操作
果然有
https://clojuredocs.org/clojure.core/flatten
但是,如果您这样做是为了了解它实际上是如何发生的,您可以在 github 上查看函数的源代码(flatten stuff),其中 stuff 是您想要展平的东西。
请注意,对于地图,您必须通过调用 seq 来使用解决方法。
(seq the-map-you-wanna-flatten-eventually)
user=> (扁平化 :name "Hubert" :age 23) ()
; Workaround for maps
user=> (flatten (seq :name "Hubert" :age 23))
(:name "Hubert" :age 23)
【讨论】:
flatten 仅适用于序列,而地图则不然。您可以使用(flatten (seq my-map))
作为一种解决方法,但它的深度不会超过一级...
@kurofune 这不太正确; flatten
适用于 sequential?
的任何内容,其中包括可能不一定是序列的事物(如向量)。
@SamEstep 感谢您的花絮【参考方案2】:
你可能想看看postwalk
这里http://clojuredocs.org/clojure.walk/postwalk
另见postwalk-demo
:http://clojuredocs.org/clojure.walk/postwalk-demo
这是一个工作程序:
(ns clj.core
(:use tupelo.core)
(:require [clojure.walk :refer [postwalk]] )
)
(def result (atom []))
(defn go [data]
(postwalk (fn [it]
(spyx it)
(when (keyword? it)
(swap! result append it))
it)
data))
(newline)
(spyx (go :a 1 :b :c 3 :d 4))
(spyx @result)
结果:
it => :a
it => 1
it => [:a 1]
it => :b
it => :c
it => 3
it => [:c 3]
it => :d
it => 4
it => [:d 4]
it => :c 3, :d 4
it => [:b :c 3, :d 4]
it => :a 1, :b :c 3, :d 4
(go :a 1, :b :c 3, :d 4) => :a 1, :b :c 3, :d 4
(clojure.core/deref result) => [:a :b :c :d]
使用你的数据,最终的输出是:
(clojure.core/deref 结果) => [:top :top :top :bottom :top :top :bottom :bottom :bottom :bottom :bottom :bottom :top :top :bottom :bottom :bottom :top :bottom :bottom]
这是一个简单的递归解决方案:
(def mm :a 1 :b :c 3 :d 4)
(defn accum
[it]
(spy :msg "accum" it)
(when (keyword? it)
(swap! result append it)))
(defn walk [data]
(spy :msg "walk" data)
(cond
(coll? data) (mapv walk data)
:else (accum data)))
(newline)
(reset! result [])
(walk mm)
(spyx @result)
有输出:
walk => :a 1, :b :c 3, :d 4
walk => [:a 1]
walk => :a
accum => :a
walk => 1
accum => 1
walk => [:b :c 3, :d 4]
walk => :b
accum => :b
walk => :c 3, :d 4
walk => [:c 3]
walk => :c
accum => :c
walk => 3
accum => 3
walk => [:d 4]
walk => :d
accum => :d
walk => 4
accum => 4
(clojure.core/deref result) => [:a :b :c :d]
【讨论】:
这是一个很好的解决方案,但我实际上是在尝试使用递归来解决问题。我确信有一种非常简单的方法可以做到这一点。【参考方案3】:看flatten的来源:
(defn flatten
"Takes any nested combination of sequential things (lists, vectors,
etc.) and returns their contents as a single, flat sequence.
(flatten nil) returns an empty sequence."
:added "1.2"
:static true
[x]
(filter (complement sequential?)
(rest (tree-seq sequential? seq x))))
您现在只需将 sequential?
更改为 coll?
即可包含地图。此外,如果您只想获取关键字,还可以添加every-pred
:
(defn flatten' [x]
(filter (every-pred (complement coll?) keyword?)
(rest (tree-seq coll? seq x))))
【讨论】:
【参考方案4】:如果您的数据嵌套不是很深(例如向下数百层),您可以简单地使用递归:
(defn my-flatten [x]
(if (coll? x)
(mapcat my-flatten x)
[x]))
在回复中:
user> (my-flatten sample)
(:top :top :top :bottom :top :top :bottom :bottom :bottom
:bottom :bottom :bottom :top :top :bottom :bottom
:bottom :top :bottom :bottom)
否则我会同意 tree-seq 在这里是非常好的变体:
user> (filter keyword? (tree-seq coll? seq sample))
(:top :top :top :bottom :top :top :bottom :bottom
:bottom :bottom :bottom :bottom :top :top :bottom
:bottom :bottom :top :bottom :bottom)
【讨论】:
我也非常喜欢 tre-seq 解决方案,它可以处理大量、深度嵌套的输入,但递归解决方案正是我所寻找的。谢谢!以上是关于如何在 Clojure 中递归展平任意嵌套的向量和映射?的主要内容,如果未能解决你的问题,请参考以下文章