My question is when entering Seq.
why is there no Seq.tail
function?
In this code that does not convert a sequence to a list, there is no Seq.tail
function available in the recursive function. Is it because Seq.initInfinte
was used to to create the sequence, or is there another reason?
open System
let readList() =
Seq.initInfinite (fun _ -> Console.ReadLine())
|> Seq.takeWhile (fun s -> (s <> ""))
|> Seq.map (fun x -> Int32.Parse(x))
let rec listLen list1 acc =
if Seq.isEmpty list1 then
acc
else
(* There is no Seq.tail available. Compile error. *)
listLen (Seq.tail list1) (acc + 1)
[<EntryPoint>]
let main argv =
let inList = (readList())
let inListLen = listLen inList 0
printfn "%A" inListLen
0 // return an integer exit code
However, this works just fine. I'm confused about why Seq.tail
is not available, but List.tail
is available.
open System
let readList() =
Seq.initInfinite (fun _ -> Console.ReadLine())
|> Seq.takeWhile (fun s -> (s <> ""))
|> Seq.map (fun x -> Int32.Parse(x))
let rec listLen list1 acc =
if List.isEmpty list1 then
acc
else
listLen (List.tail list1) (acc + 1)
[<EntryPoint>]
let main argv =
let inList = Seq.toList (readList())
let inListLen = listLen inList 0
printfn "%A" inListLen
0 // return an integer exit code
There is no specific reason, it just was never added, that's it. Though it is available in a later version of F# (there was a large effort towards regularizing collection APIs in 4.0).
However, one could offer a common-sense argument for why Seq.tail
would be marginally useful, and perhaps even dangerous. Which might actually be the reason for not adding it initially, but I don't know for sure.
You see, lists and sequences have very different representations behind the scenes.
List is a data structure that has two fields: the first element (called "head"), and the rest of the elements, which is itself just another list (called "tail"). So calling List.tail
means merely taking the second field of the data structure. No complicated processing, just taking one of the data structure's fields.
Sequence, on the other hand, is basically a function (called IEnumerable.GetEnumerator
) that returns a mutable data structure (called IEnumerator
), which can be repeatedly "kicked" (by calling IEnumerator.MoveNext
), producing the next item on each kick, and changing its internal state.
This representaron means that, in order to "drop" the first element of the sequence, one would have to take the original sequence and wrap it in another function, which, when asked to produce an IEnumerator
, would get the inner sequence's IEnumerator
, then kick it once, and then return to the caller. Something along these lines (pseudocode):
Tail(inner).GetEnumerator =
let innerE = inner.GetEnumerator()
innerE.MoveNext()
innerE
This means that, while with a list each call to tail
makes the data structure less complex (one less item, only tail remains), with sequence each call to tail
would make it more complex (one more function wrapper). What's more, if you take a tail
of a sequence several times in a row, and then iterate over the result, you'd still be iterating over the whole original sequence, even though logically it looks shorter to you.
Applying this to your specific case, your listLen
implementation based on Seq.tail
would have quadratic complexity (as opposed to the list's linear), because every time you call Seq.isEmpty
, that will effectively cause iteration up to the first non-skipped item, and each recursive call to listLen
will add another skipped item to iterate over.
For what it's worth, the standard .NET LINQ does actually have an equivalent operation - called .Skip
, and you can totally use it from F#:
open System.Linq
let seqTail (s: _ seq) = s.Skip(1)
Or, as Robert Nielsen notes in the comments, there is actually a Seq.skip
even in F# standard library (I was writing from my phone, couldn't verify it at the time):
let seqTail s = Seq.skip 1 s
Seq.tail is not available in F# 3.x. It is available in F# 4.x. I verified that your code compiles in 4.0, but not 3.1.
A whole lot of functions were added in F# 4.0, to "regularize" List, Array and Seq.
https://github.com/fsharp/fslang-design/blob/master/FSharp-4.0/ListSeqArrayAdditions.md
(I noticed Option was left out in that issue, but suspect it too got some more functions at some point.)
As to why all these functions were missing, they simply lacked time to fill in those gaps in the earlier versions.
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