【问题标题】:Clojure nested for loop with indexClojure 嵌套的带有索引的 for 循环
【发布时间】:2021-04-09 23:09:22
【问题描述】:

我一直在尝试惯用地循环遍历嵌套向量,如下所示:

[[:a 1 :b 1 :c 1] [:a 1 :b 1 :c 3] [:a 1 :b 1 :c 1]]

我还需要在找到值后返回坐标。 例如调用(find-key-value 3)应该返回[1 2]

这是我目前所拥有的,但它没有给我我需要的输出它会返回([] [] [] [] [] [1 2] [] [] []),而我只需要[1 2]

(defn find-key-value
  [array value]
  (for [x (range 0 (count array))]
    (loop [y   0
           ret []]
      (cond
        (= y (count (nth array x))) [x y]
        :else (if (= value (get-in array [x y]))
                (recur (+ 1 y) (conj ret [x y]))
                (recur (+ 1 y) ret))))))

任何人都对如何修复我的代码以获得我想要的解决方案或想到更好的方法有任何想法!

【问题讨论】:

  • 看来(find-key-value 3)应该返回坐标[1 5],而不是[1 2]
  • @AlanThompson - 在我看来[1 2] 的意思是second sub-collection (index 1) at third element of sub-collection (index 2)

标签: clojure clojurescript


【解决方案1】:

列表推导式可用于查找满足谓词的所有值的坐标:

(defn find-locs [pred coll]
  (for [[i vals] (map-indexed vector coll)
        [j val] (map-indexed vector vals)
        :when (pred val)]
    [i j]))

(find-locs #(= 3 %) [[:a 1 :b 1 :c 1] [:a 1 :b 1 :c 3] [:a 1 :b 1 :c 1]])
=> ([1 5])

(find-locs zero? [[0 1 1] [1 1 1] [1 0 1]])
=> ([0 0] [2 1])

提出的问题似乎暗示应该忽略输入中的关键字,在这种情况下答案变为:

(defn find-locs-ignore-keyword [pred coll]
  (for [[i vals] (map-indexed vector coll)
        [j val] (map-indexed vector (remove keyword? vals))
        :when (pred val)]
    [i j]))

(find-locs-ignore-keyword #(= 3 %) [[:a 1 :b 1 :c 1] [:a 1 :b 1 :c 3] [:a 1 :b 1 :c 1]])
=> ([1 2])

【讨论】:

    【解决方案2】:

    clojure 核心中有一个函数,它完全适合该任务:keep-indexed。正是indexed map + filter

    (defn find-val-idx [v data]
      (ffirst (keep-indexed
               (fn [i row]
                 (seq (keep-indexed
                       (fn [j [_ x]] (when (= v x) [i j]))
                       (partition 2 row))))
               data)))
    
    user> (find-val-idx 3 [[:a 1 :b 1 :c 1] [:a 1 :b 1 :c 3] [:a 1 :b 1 :c 1]])
    ;;=> [1 2]
    
    user> (find-val-idx 10 [[:a 1 :b 1 :c 1] [:a 1 :b 1 :c 3] [:a 1 :b 1 :c 1]])
    ;;=> nil
    
    user> (find-val-idx 1 [[:a 1 :b 1 :c 1] [:a 1 :b 1 :c 3] [:a 1 :b 1 :c 1]])
    ;;=> [0 0]
    

    【讨论】:

      【解决方案3】:

      map-indexed 有时很有帮助。请参阅 Clojure 备忘单和其他 docs listed here

      ==>您能否编辑问题以澄清搜索条件?


      以下是搜索所需答案的概述:

      (ns tst.demo.core
        (:use demo.core tupelo.core tupelo.test))
      
      (defn coords
        [data pred]
        (let [result (atom [])]
          (doseq [row (range (count data))
                  col (range (count (first data)))]
            (let [elem    (get-in data [row col])
                  keeper? (pred elem)]
              (when keeper?
                (swap! result conj [row col]))))
          (deref result)))
      
      (dotest
        (let [data [[11 12 13]
                    [21 22 23]
                    [31 32 33]]
              ends-in-2? (fn [x] (zero? (mod x 2)))]
          (is= (coords data ends-in-2?)
            [[0 1]
             [1 1]
             [2 1]])))
      

      它基于与文档相同的template project。有很多变体(例如,您可以使用 reduce 代替原子)。

      请查看上面列出的文档。

      【讨论】:

      • map-indexed 仅适用于我需要 x 和 y 的向量的一层
      【解决方案4】:
      (defn vec-to-map [v] (into {} (into [] (map vec (partition 2 v)))))
      (defn vec-vals [v] (vals (vec-to-map v)))
      (defn map-vec-index [v el] (.indexOf (vec-vals v) el))
      
      (defn find-val-coord
        ([arr val] (find-val-coord arr val 0))
        ([arr val counter]
          (let [row (first arr)
                idx (map-vec-index row val)]
             (cond (< 0 idx) [counter idx]
                   :else (recur (rest arr) val (inc counter)))))) 
      
      (find-val-coord arr 3)    ;; => [1 2]
      

      我们还可以编写函数来选择值或对应的键 给定坐标时来自数组:

      (defn vec-keys [v] (keys (vec-to-map v)))
      
      (defn get-val-coord [arr coord]
        (nth (vec-vals (nth arr (first coord))) (second coord)))
      
      (defn get-key-coord [arr coord]
        (nth (vec-keys (nth arr (first coord))) (second coord)))
      
      
      (get-val-coord arr [1 2]) ;; => 3
      (get-key-coord arr [1 2]) ;; => :c
      

      【讨论】:

        【解决方案5】:

        我可能稍微过度设计了这个答案,但这是一个基于 单循环非递归非惰性方法,适用于任意和混合级别的嵌套并且不会因递归而遭受堆栈溢出:

        (defn find-key-value [array value]
          (loop [remain [[[] array]]]
            (if (empty? remain)
              nil
              (let [[[path x] & remain] remain]
                (cond (= x value) path
                      (sequential? x)
                      (recur (into remain
                                   (comp (remove keyword?)
                                         (map-indexed (fn [i x] [(conj path i) x])))
                                   x))
                      :default (recur remain))))))
        
        (find-key-value [[:a 1 :b 1 :c 1] [:a 1 :b 1 :c 3] [:a 1 :b 1 :c 1]] 3)
        ;; => [1 2]
        
        (find-key-value [[:a 1 [[[[[:c]]]] [[[9 [[[3]] :k]] 119]]]] [:a [[[1]]] :b 1]] 3)
        ;; => [0 1 1 0 0 1 0 0 0]
        
        (find-key-value (last (take 20000 (iterate vector 3))) 3)
        ;; => [0 0 0 0 0 0 0 0 0 0 0 0 0 ...]
        

        【讨论】:

          【解决方案6】:

          一个更简单的解决方案,假设内部向量是二维数组 键值向量,使用二维数组的展平和.indexOf

          (defn find-coord [arr val]
            (let [m (count (first arr))
                  idx (.indexOf (flatten arr) val)]
              [(quot idx m) (quot (dec (mod idx m)) 2)]))
          
          (find-coord arr 3) ;;=> [1 2]
          

          【讨论】:

            猜你喜欢
            • 2020-07-07
            • 1970-01-01
            • 1970-01-01
            • 2021-08-21
            • 1970-01-01
            • 2020-12-30
            • 1970-01-01
            • 2018-07-08
            • 1970-01-01
            相关资源
            最近更新 更多