To quote Scala's documentation:
def span(p: (A) => Boolean): (Seq[A], Seq[A])
Splits this iterable collection into a prefix/suffix pair according to a predicate.
Note: c span p is equivalent to (but possibly more efficient than) (c takeWhile p, c dropWhile p), provided the evaluation of the predicate p does not cause any side-effects.
Note: might return different results for different runs, unless the underlying collection type is ordered.
When looking at the F# documentation for Seq I don't see any equivalent.
groupBy, partition, splitAt, none of them match what I'm trying to do. It's like doing a takeWhile and skipWhile at the same time but instead of needing two iterations you only need one where the function would return a tuple of (takeWhile, skipWhile).
The output should match the following function
module List
let span (predicate: 'a -> bool) (list: 'a list): ('a list * 'a list) =
(list |> List.takeWhile predicate, list |> List.skipWhile predicate)
But only need one iteration since my sequence may be infinite.
[1;2;3;4] |> List.span (fun i -> i % 2 = 1) => ([1], [2;3;4])
Advertisements. Scala Seq is a trait to represent immutable sequences. This structure provides index based access and various utility methods to find elements, their occurences and subsequences. A Seq maintains the insertion order.
A Seq is an Iterable that has a defined order of elements. Sequences provide a method apply() for indexing, ranging from 0 up to the length of the sequence. Seq has many subclasses including Queue, Range, List, Stack, and LinkedList. A List is a Seq that is implemented as an immutable linked list.
Your explanation is not what Seq.span
does in Scala: it partitions one sequence in two, putting all input elements into first tuple value as long as predicate function returns true. Once function returns false, all remaining elements will be pushed into second tuple value.
Example in F# would be like:
[1;2;3;4] |> Seq.span (fun i -> i % 2 = 1) => ([1], [2;3;4])
This can be achieved quite easily using mutualy recursive functions:
module Seq
open System.Collections.Generic
let span (predicate: 'a -> bool) (seq: 'a seq): ('a seq * 'a seq) =
let rec insertLeft predicate (e: IEnumerator<'a>) (left: ResizeArray<'a>) (right: ResizeArray<'a>) =
if e.MoveNext() then
if predicate e.Current then
left.Add e.Current
insertLeft predicate e left right
else
// once predicate returned false, all consecutive elements land in right list
right.Add e.Current
insertRight e right
and insertRight (e: IEnumerator<'a>) (right: ResizeArray<'a>) =
if e.MoveNext() then
right.Add e.Current
insertRight e right
let left = ResizeArray<_>()
let right = ResizeArray<_>()
use enumerator = seq.GetEnumerator()
insertLeft predicate enumerator left right
(upcast left, upcast right)
I have a helper function which I find quite useful for this sort of thing:
module Seq =
let groupAdjacentBy f xs =
let mutable prevKey, i = None, 0
xs
|> Seq.groupBy (fun x ->
let key = f x
if prevKey <> Some key then
i <- i + 1
prevKey <- Some key
(i, key))
|> Seq.map (fun ((_, k), v) -> (k, v))
Note that it uses locally contained mutation in the implementation because it's the simplest way to reuse the existing Seq.groupBy
.
This is actually a grouping function but it only puts items in the same group if they are adjacent to each other. To my mind this is a very general way of solving problems that need several uses of takeWhile
and skipWhile
, but simpler because it's all done in one pass. The grouping function returns a group key of any type rather than just a boolean, adding more flexibility.
Here's an example usage with a grouping function that returns a boolean:
[ 1; 2; -1; -2; 3; 4; -5 ]
|> Seq.groupAdjacentBy (fun x -> x > 0) // positive?
|> Seq.map snd
// seq [seq [1; 2]; seq [-1; -2]; seq [3; 4]; seq [-5]]
In this example the first two lines return the groups with their keys (true
, false
, true
, false
respectively). You can then use these keys in your logic, but if you don't care about them then the Seq.map snd
will discard them. And you're left with a seq<seq<int>>
as shown above.
This is what I've come up with, it's not a great answer because it requires you eagerly iterate through the first one so if you return true long enough you will blow without of memory. I will leave the question open for a few more days and if I don't see a better answer I will mark this one. Again I would really like a better answer that completely works with an infinite sequence in any situation.
module Seq
let span (predicate: 'a -> bool) (sequence: 'a seq): ('a seq * 'a seq) =
let enumerator = sequence.GetEnumerator()
let isNotDone = ref (enumerator.MoveNext())
let first = seq {
let e = enumerator
if !isNotDone then
while (!isNotDone && predicate e.Current) do
yield enumerator.Current
isNotDone := e.MoveNext() }
let second = seq {
use e = enumerator
if !isNotDone then
yield e.Current
while e.MoveNext() do
yield e.Current }
let eagerFirst = List.toSeq (Seq.toList first)
(eagerFirst, second)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With