❮❮❮ ❮❮❮   ❯❯❯ ❯❯❯
Interspersing elements into a sequence
Various approaches

Tags: fs
Reading time: 2 min.

Problem

Implement

/// Inserts after period elements the filler element into original sequence.
let intersperse (period : int) (filler : 'a) (original : seq<'a>) : seq<'a> =
  failwith "nyi

The call should work as

seq { for i in 1..5 -> i} = seq { 1; 2; 3; 4}
intersperse 0 seq { for i in 1..5 -> i} = seq { 1; 0; 2; 0; 3; 0; 4 }

With standard lib

let intersperse (period : int) (filler : 'a) (original : seq<'a>) : seq<'a> =
  if period <= 0
  then failwith $"Period must be > 0 but is %i{period}."
  original
  |> Seq.chunkBySize period
  |> Seq.mapi (fun i b ->
      seq {
        if i > 0 then yield filler 
        yield! b
      }
    )
  |> Seq.concat

With local state for first block

let intersperse (period : int) (filler : 'a) (original : seq<'a>) : seq<'a> =
  if period <= 0
  then failwith $"Period must be > 0 but is %i{period}."
  let notFirstBlock = ref false
  original
  |> Seq.chunkBySize period
  |> Seq.map (fun b ->
      seq {
        if notFirstBlock
        then notFirstBlock.Value <- true
        else yield filler 
        yield! b
      }
    )
  |> Seq.concat

With IEnumerator and without intermediate arrays

let intersperse (period : int) (filler : 'a) (original : seq<'a>) : seq<'a> =
  if period <= 0
  then failwith $"Period must be > 0 but is %i{period}."
  let enum = original.GetEnumerator ()
  let inblock = ref 0
  seq {
    if enum.MoveNext ()
    then
      while enum.MoveNext () && inblock.Value < period do
        yield enum.Current
        inblock.Value <- inblock.Value + 1
      inblock.Value <- 0
      while enum.MoveNext () do
        yield filler
        while enum.MoveNext () && inblock.Value < period do
          yield enum.Current
          inblock.Value <- inblock.Value + 1
        inblock.Value <- 0 
  }