【问题标题】:Clojure sub-sequence position in sequenceClojure 序列中的子序列位置
【发布时间】:2013-03-05 21:58:40
【问题描述】:

Clojure 是否提供任何内置方法来查找给定序列中子序列的位置?

【问题讨论】:

    标签: clojure sequences


    【解决方案1】:

    Clojure 提供了一种内置方式,方便Java Interop

    (java.util.Collections/indexOfSubList '(a b c 5 6 :foo g h) '(5 6 :foo))
    ;=> 3
    

    【讨论】:

    • 感谢您的回答。这就是我最终将使用的,但我通常会尽量避免从“业务”代码中显式调用 Java Interop,因为我发现它有点冗长。不过还是谢谢你。
    • 虽然这可能有效,但请注意集合不是序列。
    • @NielsK 哲学概念不谈,我想你会发现java.util.Listseq 的超类,并且java 方法位于java.util.Lists 对上。因此,您可以在惰性序列上使用它(注意不要评估无限序列)(java.util.Collections/indexOfSubList (range 10) (range 3 7)) ;=> 3、向量、排序映射等。
    • 这很有趣。但是,它似乎确实适用于惰性序列,但操作本身似乎并不惰性。我在(范围 10000000)(范围 999998 999999)上尝试了这两种方法,并在 Collections 方式上获得了 GC 开销限制,并且我的 find-pos 得到了正常答案。因此,必须不仅仅是纯粹的哲学概念。
    • @NielsK 你是对的。当您禁用 GC 开销检查时,这似乎确实试图实现整个内存范围。
    【解决方案2】:

    序列是抽象的,而不是具体的。可以通过序列抽象使用的某些实体有一种方法可以找到子序列的位置(例如字符串和 Java 集合),但序列通常不会,因为底层实体不必有索引.

    但是,您可以做的是创建元素标识和索引函数的并列。看看map-indexed

    这是一个简单的实现,它将懒惰地找到序列中(所有)子序列的位置。只需使用 first 或取 1 只找到一个:

    (defn find-pos
      [sq sub]
      (->>
        (partition (count sub) 1 sq)
        (map-indexed vector)
        (filter #(= (second %) sub))
        (map first)))
    
    => (find-pos  [:a :b \c 5 6 :foo \g :h]
                    [\c 5 6 :foo])
    (2)
    
    => (find-pos  "the quick brown fox"
                    (seq "quick"))
    (4)
    

    请注意,基于索引的算法通常不会在函数式语言中执行。除非有充分的理由需要在最终结果中使用索引,否则过度使用索引查找会被视为代码异味。

    【讨论】: