如何在Clojure中删除vector中的顺序匹配?

时间:2016-04-01 15:12:17

标签: algorithm vector clojure

假设我有一个向量["a" "b" "c" "a" "a" "b"]。如果给定序列["a" "b"],如何删除该序列的所有实例(按顺序)?在这里,结果只是["c" "a"]

5 个答案:

答案 0 :(得分:6)

如果事先知道需要删除的序列,core.match可能对您的任务有用:

(require '[clojure.core.match :refer [match]])

(defn remove-patterns [seq]
  (match seq
    ["a" "b" & xs] (remove-patterns xs)
    [x & xs] (cons x (remove-patterns xs))
    [] ()))


(remove-patterns ["a" "b" "c" "a" "a" "b"]) ;; => ("c" "a")

答案 1 :(得分:5)

简短的回答是将其视为字符串并执行正则表达式删除:

(defn remove-ab [v]
  (mapv str (clojure.string/replace (apply str v) #"ab" "")))

(remove-ab ["a" "b" "c" "a" "a" "b"])
=> ["c" "a"]

很长的答案是通过迭代序列,识别匹配并返回没有它们的序列来实现自己的正则表达式状态机。

Automat可以帮助您制作自己的低级正则表达式状态机: https://github.com/ztellman/automat

Instaparse可用于制作丰富的语法: https://github.com/Engelberg/instaparse

你真的不需要一个这么小的匹配库,你可以把它作为一个循环实现:

(defn remove-ab [v]
  (loop [[c & remaining] v
         acc []
         saw-a false]
    (cond
     (nil? c) (if saw-a (conj acc "a") acc) ;; terminate
     (and (= "b" c) saw-a) (recur remaining acc false)  ;; ignore ab
     (= "a" c) (recur remaining (if saw-a (conj acc "a") acc) true) ;; got a
     (and (not= "b" c) saw-a) (recur remaining (conj (conj acc "a") c) false) ;; keep ac
     :else (recur remaining (conj acc c) false)))) ;; add c

但是,正确处理所有条件可能会非常棘手......因此正式的正则表达式或状态机是有利的。

或递归定义:

(defn remove-ab [[x y & rest]]
  (cond
   (and (= x "a") (= y "b")) (recur rest)
   (nil? x) ()
   (nil? y) [x]
   :else (cons x (remove-ab (cons y rest)))))

答案 2 :(得分:2)

2元素子序列的递归解决方案:

(defn f [sq [a b]]
  (when (seq sq)
    (if 
      (and
        (= (first sq) a)
        (= (second sq) b))
      (f (rest (rest sq)) [a b]) 
      (cons (first sq) (f (rest sq) [a b])))))

没有经过详尽的测试,但似乎有效。

答案 3 :(得分:1)

使用lazy-seqtakedrop处理任何有限子项以及需要过滤的任何(包括无限)序列的简单解决方案:

(defn remove-subseq-at-start
  [subseq xs]
  (loop [xs xs]
    (if (= (seq subseq) (take (count subseq) xs))
      (recur (drop (count subseq) xs))
      xs)))

(defn remove-subseq-all [subseq xs]
  (if-let [xs (seq (remove-subseq-at-start subseq xs))]
    (lazy-seq (cons (first xs) (remove-subseq subseq (rest xs))))
    ()))

(deftest remove-subseq-all-test
  (is (= ["c" "a"] (remove-subseq-all ["a" "b"] ["a" "b" "a" "b" "c" "a" "a" "b"])))
  (is (= ["a"] (remove-subseq-all ["a" "b"] ["a"])))
  (is (= ["a" "b"] (remove-subseq-all [] ["a" "b"])))
  (is (= [] (remove-subseq-all ["a" "b"] ["a" "b" "a" "b"])))
  (is (= [] (remove-subseq-all ["a" "b"] nil)))
  (is (= [] (remove-subseq-all [] [])))
  (is (= ["a" "b" "a" "b"] (->> (remove-subseq-all ["c" "d"] (cycle ["a" "b" "c" "d"]))
                                (drop 2000000)
                                (take 4))))

  (is (= (seq "ca") (remove-subseq-all "ab" "ababcaab"))))

答案 4 :(得分:0)

如果可以确保输入是向量,我们可以使用subvec检查每个元素,以下相同长度的子向量是否与模式匹配。如果是这样,我们省略它,否则我们继续前进到向量中的下一个元素:

(let [pattern ["a" "b"]
      source ["a" "b" "c" "a" "a" "b"]]
    (loop [source source
           pattern-length (count pattern)
           result []]
        (if (< (count source) pattern-length)
            (into [] (concat result source))
            (if (= pattern (subvec source 0 pattern-length))
              ; skip matched part of source
              (recur (subvec source pattern-length) pattern-length result)
              ; otherwise move ahead one element and save it as result
              (recur (subvec source 1) pattern-length 
                     (conj result (first source)))))))

使用一般序列,您可以使用相同的方法,在适当时替换takedrop