【问题标题】:How to find indexes in deeply nested data structure(vectors and lists) in Clojure?如何在 Clojure 的深度嵌套数据结构(向量和列表)中查找索引?
【发布时间】:2017-08-21 20:32:24
【问题描述】:

我正在尝试从嵌套数据结构中获取值的索引路径。

我写了一些代码,但它不能正常工作

(defn helper
  [val form c index]
  (loop [f form
         i index
         l 0]
    (if (seq f)
      (if (or (list? (first f)) (vector? (first f)))
        (helper val (first f) (conj c l) (inc i))
        (if (= val (first f))
          (conj c l)
          (recur (rest f) (inc i) (inc l))))
      nil)))

(defn find-indexes
  [val form c index]
  (when (seq form)
    (if-let [result (helper val form c index)]
      result
      (find-indexes val (rest form) c (inc index)))))

(defn find-index-route
  [val form]
  (find-indexes val form [] 0))

当前行为:

(find-index-route :my-key '(1 2 ("a" :my-key))) ;=> [2 1] "Works"

(find-index-route :my-key '(1 2 ("a" ["b" 3 :my-key]))) ;=> [2 1 2] "Works"

(find-index-route :my-key '(1 2 ("a" ["b" 3 () :my-key]))) ;=> nil "Does NOT Work"

(find-index-route :my-key '(1 2 ("a" [] ["b" 3 :my-key]))) ;=> nil "Does NOT Work"

(find-index-route :my-key '(1 2 [] ("a" ["b" 3 :my-key]))) ;=> [0 1 2] "It has to be [3 1 2]"

问题是,如果函数在找到返回值之前命中空列表或向量,则返回nil 或0(仅针对第一级)

我需要的行为:

                                          ;=> [indexes...]

(find-index-route :my-key '(1 2 :my-key)) ;=> [2]

(find-index-route :my-key '(1 2 "a" :my-key "b")) ;=> [3]

(find-index-route :my-key '(1 2 [:my-key] "c")) ;=> [2 0]

(find-index-route :my-key '(1 2 [3 [:my-key]])) ;=> [2 1 0]

(find-index-route :my-key '(1 2 [3 [[] :my-key]])) ;=> [2 1 1]

(find-index-route :my-key '(1 2 [3 [4 5 6 (:my-key)]])) ;=> [2 1 3 0]

(find-index-route :my-key '(1 2 [3 [[]]])) ;=> nil or []

【问题讨论】:

  • 这个问题在问什么?什么是“索引路线”?为什么:my 很特别?你输入的结构是什么?您的预期输出是什么意思?正如所写,这个问题是无法回答的。
  • @amalloy 编辑的问题,现在更清楚了吗?
  • 请包含您尝试的实际代码。 ... 并不能说服我们您实际上尝试过任何东西。
  • @NathanDavis 我已经添加了我的实际代码。

标签: clojure


【解决方案1】:
(defn find-index-route [x coll]
  (letfn [(path-in [y]
            (cond
              (= y x) '()
              (coll? y) (let [[failures [success & _]]
                              (->> y
                                   (map path-in)
                                   (split-with not))]
                          (when success (cons (count failures) success)))))]
    (path-in coll)))

例子:

(find-index-route :my-key '(1 2 :my-key)) ;=> [2]
=> (2)

(find-index-route :my-key '(1 2 "a" :my-key "b")) ;=> [3]
=> (3)

(find-index-route :my-key '(1 2 [:my-key] "c")) ;=> [2 0]
=> (2 0)

(find-index-route :my-key '(1 2 [3 [:my-key]])) ;=> [2 1 0]
=> (2 1 0)

(find-index-route :my-key '(1 2 [3 [[] :my-key]])) ;=> [2 1 1]
=> (2 1 1)

(find-index-route :my-key '(1 2 [3 [4 5 6 (:my-key)]])) ;=> [2 1 3 0]
=> (2 1 3 0)

(find-index-route :my-key '(1 2 [3 [[]]])) ;=> nil or []
=> nil

考虑性能

split-with 使用take-whiledrop-while 扫描序列两次,以生成两个惰性序列。如果性能很紧张,您可以编写一个 drop-while 版本,它还可以告诉您它已经丢弃了多少项目:

(defn counted-drop-while [pred coll]
  (loop [takes 0, tail coll]
    (if (and (seq tail) (pred (first tail)))
      (recur (inc takes) (rest tail))
      [takes tail])))

它只进行一次扫描。我们可以很容易地适应find-index-route 来使用它:

(defn find-index-route [x coll]
  (letfn [(path-in [y]
            (cond
              (= y x) '()
              (coll? y) (let [[fail-count [success & _]]
                              (->> y
                                   (map path-in)
                                   (counted-drop-while not))]
                          (when success (cons fail-count success)))))]
    (path-in coll)))

... 结果相同。

【讨论】:

  • 非常感谢,对我来说这似乎并不难,但实际上我花了太多时间:)
  • @ErtuğrulÇetin 我必须承认我喜欢解构split-with 产生所需索引和成功的方式。
  • 您的解决方案非常优雅且实用的惯用 Clojure 风格我的方法过于迫切,是的,拆分似乎真的很酷:)
  • [success & _] 中的& _ 是什么原因?如果您要丢弃除第一个以外的所有内容,为什么不只使用[success] 抓住第一个?
  • @ChrisMurphy 可能会列出一长串他只想得到第一个
【解决方案2】:

这个问题对于 Spectre 库来说似乎是完美的,它是关于导航和转换嵌套数据结构的。我昨天在 Clojurians 上向 Nathan Marz 询问了这个问题的可能解决方案 - 请参阅他的原始答案 here

(defn find-index-route [v data]
  (let [walker (recursive-path [] p
                               (if-path sequential?
                                        [INDEXED-VALS
                                         (if-path [LAST (pred= v)]
                                                  FIRST
                                                  [(collect-one FIRST) LAST p])]))
        ret    (select-first walker data)]
    (if (vector? ret) ret [ret])))

请注意,如果 :my-key 不存在,则返回 [nil]。您可以通过将最后一个“if”部分更改为以下内容来轻松修改它以返回 nil:

(if (or (vector? ret) (nil? ret)) ret [ret])

【讨论】:

    【解决方案3】:

    我制作了一个找到所有路线的版本。

    (defn find-index-route
      "find all routes to some value"
      ([k x] (find-index-route k x []))
      ([k x p]
       (cond
         (= k x) [p]
         (coll? x) (->> (if (map? x) x (vec x))
                        (reduce-kv (fn [acc i v]
                                     (into (vec (find-index-route k v (conj p i))) acc)) []))
         :else nil)))
    

    编辑:也适用于地图

    (find-index-route :my-key '{:bar 33
                                :foo [{:my-key ("0" :my-key)}]
                                "bar" ["key" {:foo ([:my-key])}]})
    ;=> [["bar" 1 :foo 0 0] [:foo 0 :my-key 1]]
    

    【讨论】:

    • 什么是 p?你能展示一些使用你的函数的例子吗?
    • p 是实际路径/路线。 (= k x) [p] 返回一个带有实际路线的向量。 nil 表示“这条路线没用”
    【解决方案4】:

    我还找到了另一个解决方案:

    (defn find-index-route
      [x form]
      (letfn [(get-nodes [form]
                (tree-seq coll? identity form))
    
              (get-tree [form]
                (rest (get-nodes form)))
    
              (get-level [form]
                (if (or (not (coll? form)) (not (seq form)))
                  0
                  (count (filter coll? (get-nodes form)))))
    
              (get-result [x form]
                (reduce (fn [v form]
                          (let [[idx lvl _] (last v)
                                form-lvl  (get-level form)
                                contains? ((set (get-nodes form)) x)]
                            (conj v [(if (or (not idx) (< form-lvl lvl)) 0 (+ idx 1))
                                     form-lvl
                                     contains?]))) [] (get-tree form)))
    
              (get-indices [x form]
                (map (fn [[idx _ _]] idx)
                     (filter
                       (fn [[_ _ contains?]] contains?) (get-result x form))))]
    
        (get-indices x form)))
    

    【讨论】:

    • @Thumbnail 您的解决方案更快更简洁,但我终于自己解决了,我想得太迫切了。
    猜你喜欢
    • 1970-01-01
    • 2020-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-10
    • 1970-01-01
    • 1970-01-01
    • 2015-01-08
    相关资源
    最近更新 更多