For a final challenge, here is what you can do after the book. The Standard Library has methods for splitting a string over a single character, or a function that takes a single character and returns a Boolean. However, it doesn't have any methods for splitting a string over a substring, or doing it lazily.

Create a new method, which can be used on lazy strings and substrings, and takes a separator (String) and optionally String.CompareOptions and Locale, and returns a lazy sequence of the ranges between each occurrence of the separator in the original string/substring.

There are several ways of achieving this. The following hints describe one solution which uses some of the methods we have created in this course. Try and see if you can complete this by using as few hints as possible.

Hints:

  • We can find the ranges of the separators first, and then invert them to get the ranges of the spaces between the separators.
  • Use the lazy allranges method we created in Lesson 6.
  • Break up the lowerBound and upperBound of the ranges of the separators into a sequence of indices.
  • Create a new sequence, still lazy, from the start index of the original string/substring, the indices from the previous hint, and the end index.
  • There is no built-in way of joining sequences of different types lazily together. Here is one way:
    private func joinSequences<S1,S2>(_ s1: S1, _ s2: S2)
      -> UnfoldSequence<S1.Element, (Optional<S1.Iterator>, S2.Iterator)>
      where S1:Sequence, S2:Sequence, S1.Element == S2.Element {
        return sequence(state: (Optional(s1.makeIterator()), s2.makeIterator()))
        { seqs -> S1.Element? in
          guard let _ = seqs.0 else { return seqs.1.next() }
          return seqs.0?.next()
            ?? { seqs.0 = nil; return seqs.1.next() }()
        }
    }
    
    public func +<S1,S2>(s1: S1, s2: S2)
      -> UnfoldSequence<S1.Element, (Optional<S1.Iterator>, S2.Iterator)>
      where S1:Sequence, S2:Sequence, S1.Element == S2.Element {
        return joinSequences(s1, s2)
    }
    
    public func +<S1,S2>(s1: S1, s2: S2)
      -> LazySequence<UnfoldSequence<S1.Element, (Optional<S1.Iterator>, S2.Iterator)>>
      where S1:Sequence, S2:LazySequenceProtocol, S1.Element == S2.Element {
        return joinSequences(s1, s2).lazy
    }
    
    public func +<S1,S2>(s1: S1, s2: S2)
      -> LazySequence<UnfoldSequence<S1.Element, (Optional<S1.Iterator>, S2.Iterator)>>
      where S1:LazySequenceProtocol, S2:Sequence, S1.Element == S2.Element {
        return joinSequences(s1, s2).lazy
    }
  • Take the new sequence, flatten it if necessary, and group two and two indices together.
  • Use the group2 method from Lesson 6.
  • Create ranges from these grouped indices.
  • Return this as a lazy sequence.