【问题标题】:Clojure - Remove items from vector inside loopClojure - 从循环内的向量中删除项目
【发布时间】:2016-08-14 23:51:48
【问题描述】:

我刚开始学习 Clojure 和函数式编程,但在尝试实现以下任务时遇到了困难:

我有一个这样的向量向量 [[a b] [a c] [b c] [c d] [d b]]。我需要遍历它,删除已经出现在第二列的第二列中的项目。例如项目 [b c] 和 [d b](因为 c 和 b 都已出现在第二列)。我设法获得了一个当时删除一个项目的函数,但我需要遍历每个项目检查和删除项目的向量。我怎样才能做到这一点?我考虑过使用递归来实现这一点,但每次尝试都以失败告终

例如

输入: [[a b] [a c] [b c] [c d] [a d] [b e]]

输出(预期): [[a b] [a c] [c d] [b e]]

删除的项目: [[b c] [a d]]

如您所见,c 和 d 已经分别出现在前面的项目 [a c] 和 [c d] 上,所以我必须删除项目 [b c] 和 [a d]。

到目前为止,我有以下代码

此函数返回要删除的项目的向量。在我们的场景中,它返回向量 [[b c] [a d]]

(defn find-invalid [collection-data item-to-check]
    (subvec (vec (filter #(= (second %) item-to-check) collection-data)) 1))

(defn find-invalid [collection-data item-to-check]
    (subvec (vec (filter #(= (second %) item-to-check) collection-data)) 1))

这个其他函数一次从原始向量中删除一个项目,按项目的给定索引

(defn remove-invalid [collection-data item-position]
    (vec (concat (subvec collection-data 0 item-position) (subvec collection-data (inc item-position)))))

最后一个函数是我用来测试这个逻辑的

(defn remove-invalid [original-collection ]
    (dorun (for [item original-collection]
       [
        (dorun (for [invalid-item (find-invalid original-collection (second item))]
                 [
                  (cond (> (count invalid-item) 0)
                        (println (remove-invalid original-collection (.indexOf original-collection invalid-item)))
                        )
                  ]))
        ])))

我认为递归可以解决我的问题,但如果能帮我解决这个问题,我将不胜感激:)。

提前致谢。

【问题讨论】:

  • 好问题。我相信 clojure 专业人士会来救援 =]

标签: recursion vector clojure functional-programming


【解决方案1】:

实现这一点的一种方法是使用reduce

(first (reduce (fn [[result previous] [a b]]
                 [(if (contains? previous b)
                    result
                    (conj result [a b]))
                  (conj previous b)])
               [[] #{}]
               '[[a b] [a c] [b c] [c d] [d b]]))
;=> [[a b] [a c] [c d]]

我们想要跟踪到目前为止我们建立的结果 (result) 以及我们之前在第二列 (previous) 中找到的一组项目。对于每个新项目[a b],我们检查previous 是否包含第二个项目b。如果是这样,我们不会向我们的result 添加任何内容。否则,我们将conj 新项目[a b] 放到result 的末尾。我们还将conj 第二项b 转换为previous。因为previous 是一个集合,所以如果previous 已经包含b,这将不起作用。最后,在reduce 完成后,我们从结果中取出first 项,它代表了我们的最终答案。

【讨论】:

    【解决方案2】:

    如果我正确理解您的问题,应该这样做:

    (defn clear [v]
      (loop [v v existing #{} acc []]
        (if (empty? v)
          acc
          (recur (rest v)
                 (conj existing (second (first v)))
                 (if (some existing [(ffirst v)]) acc (conj acc (first v)))))))
    

    用循环/递归解决。如果我有时间,我会看看我是否可以在这里使用 reduce 或任何合适的功能。

    此过滤器:[["a" "b"] ["a" "c"] ["b" "c"] ["c" "d"] ["d" "b"]][["a" "b"] ["a" "c"]]

    【讨论】:

      【解决方案3】:

      如果您可以像示例中那样依赖连续的重复项,请使用

      (->> '[[a b] [a c] [b c] [c d] [a d] [b e]]
           (partition-by second)
           (map first))
      ;-> ([a b] [a c] [c d] [b e])
      

      否则,基于 Clojures distinct 转换器实现 distinct-by 转换器。

      (sequence (distinct-by second)
                '[[a b] [a c] [b c] [c d] [a d] [b e]])
      
      ;-> ([a b] [a c] [c d] [b e])
      

      实施

      (defn distinct-by [f]
         (fn [rf]
           (let [seen (volatile! #{})]
             (fn
               ([] (rf))
               ([result] (rf result))
               ([result input]
                (let [vinput (f input)] ; virtual input as seen through f
                  (if (contains? @seen vinput)
                    result
                    (do (vswap! seen conj vinput)
                        (rf result input)))))))))
      

      【讨论】:

        【解决方案4】:

        以下类似于@Elogent's answer,但使用:as子句避免重构事物:

        (defn filtered [stuff]
          (second
           (reduce
            (fn [[seconds ans :as sec-ans] [x y :as xy]]
              (if (seconds y)
                sec-ans
                [(conj seconds y) (conj ans xy)]))
            [#{} []]
            stuff)))
        

        例如,

        (filtered '[[a b] [a c] [b c] [c d] [d b]])
        ;[[a b] [a c] [c d]]
        

        【讨论】:

          【解决方案5】:

          只是为了好玩: 这些不保留结果的顺序,但如果你没问题,它们会很有表现力(重复项可以按任何顺序排列,与上面的partition-by 变体不同):

          一个是按第二个值对所有内容进行分组,并从每个 val 中获取第一项:

          (map (comp first val)
               (group-by second '[[a b] [a c] [b c] [c d] [a d] [b e]]))
          
          ;; => ([a b] [a c] [c d] [b e])
          

          还有一个很好的方法,使用排序集:

          (into (sorted-set-by #(compare (second %1) (second %2)))
                '[[a b] [a c] [b c] [c d] [a d] [b e]])
          ;; => #{[a b] [a c] [c d] [b e]}
          

          还有一个,也不保留顺序:

          (vals (into {} (map (juxt second identity)
                              (rseq '[[a b] [a c] [b c] [c d] [a d] [b e]]))))
          ;; => ([b e] [c d] [a c] [a b])
          

          但是是的,我猜循环/递归总是更快:

          (defn remove-dupes [v]
            (loop [[[_ i2 :as pair] & xs :as v] v present #{} res []]
              (cond (empty? v) res
                    (present i2) (recur xs present res)
                    :else (recur xs (conj present i2) (conj res pair)))))
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-06-24
            • 1970-01-01
            • 1970-01-01
            • 2013-04-22
            • 2012-01-27
            • 1970-01-01
            • 2012-05-08
            相关资源
            最近更新 更多