5

Is there an easy / idiomatic way in Clojure to test whether a given sequence is included within another sequence? Something like:

(subseq? [4 5 6] (range 10))  ;=> true
(subseq? [4 6 5] (range 10))  ;=> false
(subseq? "hound" "greyhound") ;=> true

(where subseq? is a theoretical function that would do what I'm describing)

It seems that there is no such function in the core or other Clojure libraries... assuming that's true, is there a relatively simple way to implement such a function?

Dave Yarwood
  • 2,866
  • 1
  • 17
  • 29

3 Answers3

12
(defn subseq? [a b]
  (some #{a} (partition (count a) 1 b)))
amalloy
  • 89,153
  • 8
  • 140
  • 205
  • the most used piece of code here https://exercism.org/tracks/clojure/exercises/sublist/solutions – VP. Nov 23 '22 at 10:52
2
(defn subseq? [target source] 
  (pos? (java.util.Collections/indexOfSubList (seq source) (seq target))))
A. Webb
  • 26,227
  • 1
  • 63
  • 95
  • This is good, but it will use Java's notion of equality rather than Clojure's. eg, `(subseq [[1]] '((1)))` will return false rather than true. – amalloy Feb 03 '14 at 22:08
-1

***

DISCLAIMER EDIT

This proposal is not reliable for reasons discussed in comments section.

***

@amalloy 's solution has one flaw - it won't work with infinite lazy sequences. So it will loop forever when you run this: (subseq? [1 2 3] (cycle [2 3 1]))

I propose this implementation to fix this:

(defn- safe-b
  "In case b is a cycle, take only two full cycles -1 of a-count
  to prevent infinite loops yet still guarantee finding potential solution."
  [b a-count]
  (take
    (* 2 a-count)
    b))

(defn subseq? [a b]
  (let [a-count (count a)]
    (some #{a} (partition a-count 1 (safe-b b a-count)))))

=> #'user/safe-b
=> #'user/subseq?

(subseq? [1 2 3] (cycle [2 3 1]))
=> [1 2 3]
(subseq? [1 2 3] (cycle [3 2 1]))
=> nil
(subseq? [1 2 3] [2 3])
=> nil
(subseq? [2 3] [1 2 3])
=> [2 3]
  • The solution you're improving is mine, not Dave Yarwood's. And this improvement doesn't work anyway. If you search any finite subsequence of the haystack, you risk missing the needle if it occurred outside of that subsequence. For example, consider `(subseq? [1] [2 2 2 1])`. My solution isn't efficient, but this isn't the way to improve it. Also, it works fine when called as `(subseq? [1 2 3] (cycle [2 3 1]))`, so I'm not sure what you're trying to fix. – amalloy Oct 24 '21 at 09:42
  • Ah, you are right. About both things actually. Unfortunately I'm unable to recover the error I had earlier today. Well, thanks for your reply, anyway! – Damian Hryniewicz Oct 24 '21 at 18:59
  • Oh yes, I found the problem. It does work when it does find a solution. But will loop forever if it doesn't find one: `(subseq? [1 2 3] (cycle [3 2 1])` – Damian Hryniewicz Oct 24 '21 at 19:04
  • Perhaps the conclusion is that it's not wise to search for a subseq in an infinite sequence :) – Damian Hryniewicz Oct 24 '21 at 19:07
  • Right, any correct solution will never terminate when given an infinite sequence that doesn't contain the searched-for subsequence. You can only ever get "Yes" and "Maybe" for an answer, never "No". – amalloy Oct 24 '21 at 23:58