【发布时间】:2016-07-21 11:13:58
【问题描述】:
假设我有一个向量["a" "b" "c" "a" "a" "b"]。如果给定一个序列["a" "b"],我怎样才能删除该序列的所有实例(按顺序)?在这里,结果就是["c" "a"]。
【问题讨论】:
假设我有一个向量["a" "b" "c" "a" "a" "b"]。如果给定一个序列["a" "b"],我怎样才能删除该序列的所有实例(按顺序)?在这里,结果就是["c" "a"]。
【问题讨论】:
如果事先知道需要删除的序列,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")
【讨论】:
简短的回答是将其视为字符串并进行正则表达式删除:
(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)))))
【讨论】:
lazy-seq、take 和drop 来实现它——请看我的回答。
二元子序列的递归解:
(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])))))
没有经过详尽的测试,但似乎可以工作。
【讨论】:
使用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"))))
【讨论】:
如果你能确保输入是一个向量,我们可以使用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。
【讨论】: