假设我有一个向量["a" "b" "c" "a" "a" "b"]
。如果给定序列["a" "b"]
,如何删除该序列的所有实例(按顺序)?在这里,结果只是["c" "a"]
。
答案 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-seq
,take
和drop
处理任何有限子项以及需要过滤的任何(包括无限)序列的简单解决方案:
(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)))))))
使用一般序列,您可以使用相同的方法,在适当时替换take
和drop
。