Clojure - 从循环中的向量中删除项目

时间:2016-04-21 16:20:26

标签: recursion vector clojure functional-programming

我刚刚开始学习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)))
                        )
                  ]))
        ])))

我认为递归可以解决我的问题,但我会感谢任何帮助来完成这项工作:)。

提前致谢。

5 个答案:

答案 0 :(得分:3)

实现此目的的一种方法是使用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完成后,我们从结果中获取{{1}}项,这代表了我们的最终答案。

答案 1 :(得分: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"]]

答案 2 :(得分:2)

如果您可以依赖示例中的重复项,请使用

(->> '[[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)))))))))

答案 3 :(得分:1)

以下内容与@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]]

答案 4 :(得分:0)

只是为了好玩: 这些不保留结果的顺序,但是如果你没关系,那么它们非常具有表现力(重复可以是任何顺序,与上面的// unknown content closure let someHashBashing : (String) -> Bool = { return $0 == "axk" } // setup alphabet let alphabet = [Character]("abcdefghijklmnopqrstuvwxyz".characters) // any success for 500 attempts? let firstAttempt = bruteForce(someHashBashing, forAlphabet: alphabet, forWordLength: 3, forNumberOfAttempts: 500) if let password = firstAttempt.1 { print("Password cracked: \(password) (attempt \(firstAttempt.0))") } // if not, try another 500? else { if case (let i, .Some(let password)) = bruteForce(someHashBashing, forAlphabet: alphabet, forWordLength: 3, forNumberOfAttempts: 500, startingFrom: firstAttempt.0) { print("Password cracked: \(password) (attempt \(i))") } /* Password cracked: axk (attempt 608) */ } 变体不同):

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

partition-by

使用有序集合还有一种很好的方法:

(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]}

但是,我认为loop / recur总是更快:

(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])