Clojure - 返回向量向量的所有索引的函数

时间:2017-06-20 15:33:52

标签: clojure

如果我有一个向量[[[1 2 3] [4 5 6] [7 8 9]] [[10 11] [12 13]] [[14] [15]]]

如何返回向量中每个元素的位置?

例如,1具有索引[0 0 0],2具有索引[0 0 1]等,

我想要像

这样的东西
(some-fn [[[1 2 3] [4 5 6] [7 8 9]] [[10 11] [12 13]] [[14] [15]]] 1)
=> [0 0 0]

我知道如果我有一个向量[1 2 3 4],我可以做(.indexOf [1 2 3 4] 1) => 0但是如何将它扩展到向量中的向量。

由于

6 个答案:

答案 0 :(得分:2)

也许是这样的。

与Asthor的答案不同,它适用于任何嵌套深度(直到它用完堆栈)。他们的回答将给出所有匹配项目的索引,而我的回答将返回第一个。您想要哪一个取决于具体的用例。

(defn indexed [coll]
  (map-indexed vector coll))

(defn nested-index-of [coll target]
  (letfn [(step [indices coll]
            (reduce (fn [_ [i x]]
                      (if (sequential? x)
                        (when-let [result (step (conj indices i) x)]
                          (reduced result))
                        (when (= x target)
                          (reduced (conj indices i)))))
                    nil, (indexed coll)))]
    (step [] coll)))

(def x [[[1 2 3] [4 5 6] [7 8 9]] [[10 11] [12 13]] [[14] [15]]])

(nested-index-of x 2) ;=> [0 0 1]

(nested-index-of x 15) ;=> [2 1 0]

编辑:目标永远不会改变,因此内部步骤fn不需要它作为参数。

编辑2:因为我在这里拖延,递归是一个很好的谜题,也许你想要所有匹配的索引。

你可以略微调整我的第一个函数来携带累加器。

(defn nested-indices-of [coll target]
  (letfn [(step [indices acc coll]
            (reduce (fn [acc [i x]]
                      (if (sequential? x)
                        (step (conj indices i) acc x)
                        (if (= x target)
                          (conj acc (conj indices i))
                          acc)))
                    acc, (indexed coll)))]
    (step [] [] coll)))

(def y [[[1 2 3] [4 5 6] [7 8 9]] [[10 11] [12 13]] [[14] [15 [16 17 4]]]])

(nested-indices-of y 4) ;=> [[0 1 0] [2 1 1 2]]

答案 1 :(得分:2)

还有一个拉链解决方案:

(require '[clojure.zip :as z])

(defn find-in-vec [x data] 
  (loop [curr (z/vector-zip data)]
    (cond (z/end? curr) nil
          (= x (z/node curr)) (let [path (rseq (conj (z/path curr) x))]
                                (reverse (map #(.indexOf %2 %1) path (rest path))))
          :else (recur (z/next curr)))))

user> (find-in-vec 11 data)
(1 0 1)
user> (find-in-vec 12 data)
(1 1 0)
user> (find-in-vec 18 data)
nil
user> (find-in-vec 8 data)
(0 2 1)

我们的想法是对项目进行深度优先搜索,然后重新构建它的路径,为其编制索引。

答案 2 :(得分:0)

向量中的向量与向量中的int没有区别:

(.indexOf [[[1 2 3] [4 5 6] [7 8 9]] [[10 11] [12 13]] [[14] [15]]] [[14] [15]])
;;=> 2 

以上内容可能有点难以阅读,但[[14] [15]]是第三个元素。

答案 3 :(得分:0)

这样的东西
(defn indexer [vec number]
  (for [[x set1] (map-indexed vector vec) 
        [y set2] (map-indexed vector set1) 
        [z val] (map-indexed vector set2) 
        :when (= number val)]
   [x y z]))

直接写入此处未经测试。给出更多关于这个用途的背景可能会更容易给出一个好的答案,因为这感觉就像你不应该在Clojure中做的那样。

您也可以尝试以某种方式展平向量

答案 4 :(得分:0)

另一种解决方案,用于查找给定数字的每次出现的路径。

通常通过功能编程,您可以选择更广泛,通用,优雅,一口大小的解决方案。您将始终能够根据需要使用语言结构或技术进行优化(尾递归,使用累加器,使用lazy-seq等)

(defn indexes-of-value [v coll]
  (into []
        (comp (map-indexed #(if (== v %2) %1))
              (remove nil?))
        coll))

(defn coord' [v path coll]
  (cond
    ;; node is a leaf: empty or coll of numbers
    (or (empty? coll)
        (number? (first coll)))
    (when-let [indexes (seq (indexes-of-value v coll))]
      (map #(conj path %) indexes))

    ;; node is branch: a coll of colls
    (coll? (first coll))
    (seq (sequence (comp (map-indexed vector)
                         (mapcat #(coord' v (conj path (first %)) (second %))))
                   coll))))

(defn coords [v coll] (coord' v [] coll))

执行示例:

(def coll [[2 1] [] [7 8 9] [[] [1 2 2 3 2]]])
(coords 2 coll)
=> ([0 0] [3 1 1] [3 1 2] [3 1 4])

作为奖励,您可以编写一个函数来测试路径是否全部有效:

(defn valid-coords? [v coll coords]
  (->> coords
       (map #(get-in coll %))
       (remove #(== v %))
       empty?))

并尝试使用clojure.spec生成的输入解决方案:

(s/def ::leaf-vec (s/coll-of nat-int? :kind vector?))
(s/def ::branch-vec (s/or :branch (s/coll-of ::branch-vec :kind vector?
                                                          :min-count 1)
                          :leaf ::leaf-vec))
(let [v 1
      coll (first (gen/sample (s/gen ::branch-vec) 1))
      res (coords v coll)]
  (println "generated coll: " coll)
  (if-not (valid-coords? v coll res)
    (println "Error:" res)
    :ok))

答案 5 :(得分:-1)

这是一个可以递归搜索目标值的函数,跟踪索引:

(ns tst.clj.core
  (:use clj.core tupelo.test)
  (:require [tupelo.core :as t] ))
(t/refer-tupelo)

(defn index-impl
  [idxs data tgt]
  (apply glue
    (for [[idx val] (zip (range (count data)) data)]
      (let [idxs-curr (append idxs idx)]
           (if (sequential? val)
             (index-impl idxs-curr val tgt)
             (if (= val tgt)
               [{:idxs idxs-curr :val val}]
               [nil]))))))

(defn index [data tgt]
  (keep-if not-nil? (index-impl [] data tgt)))

(dotest
  (let [data-1 [1 2 3]
        data-2 [[1 2 3]
                [10 11]
                []]
        data-3 [[[1 2 3]
                 [4 5 6]
                 [7 8 9]]
                [[10 11]
                 [12 13]]
                [[20]
                 [21]]
                [[30]]
                [[]]]
  ]
    (spyx (index data-1 2))
    (spyx (index data-2 10))
    (spyx (index data-3 13))
    (spyx (index data-3 21))
    (spyx (index data-3 99))
  ))

结果:

(index data-1  2) => [{:idxs [1], :val 2}]
(index data-2 10) => [{:idxs [1 0], :val 10}]
(index data-3 13) => [{:idxs [1 1 1], :val 13}]
(index data-3 21) => [{:idxs [2 1 0], :val 21}]
(index data-3 99) => []

如果我们添加重复值,我们会得到以下结果:

data-4 [[[1 2 3]
         [4 5 6]
         [7 8 9]]
        [[10 11]
         [12  2]]
        [[20]
         [21]]
        [[30]]
        [[2]]]

(index data-4 2) => [{:idxs [0 0 1], :val 2} 
                     {:idxs [1 1 1], :val 2}
                     {:idxs [4 0 0], :val 2}]