【问题标题】:How to remove sequential matches in vector in Clojure?如何在 Clojure 中删除向量中的顺序匹配?
【发布时间】:2016-07-21 11:13:58
【问题描述】:

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

【问题讨论】:

    标签: algorithm vector clojure


    【解决方案1】:

    如果事先知道需要删除的序列,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")
    

    【讨论】:

    • 递归解决方案的常用提醒,当集合很大时有爆栈的危险......
    【解决方案2】:

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

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

    【讨论】:

    • 谢谢,可能应该提到这实际上是我目前正在做的事情,但我不确定是否有更高效的东西可以将所有内容保存为集合。自动格式化看起来很适合制作 fsm,谢谢!
    • 您可以只使用lazy-seqtakedrop 来实现它——请看我的回答。
    【解决方案3】:

    二元子序列的递归解:

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

    没有经过详尽的测试,但似乎可以工作。

    【讨论】:

      【解决方案4】:

      使用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"))))
      

      【讨论】:

        【解决方案5】:

        如果你能确保输入是一个向量,我们可以使用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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-07-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多