Why Seq.tail is not an option

前端 未结 2 535
悲&欢浪女
悲&欢浪女 2021-01-19 04:06

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 <

相关标签:
2条回答
  • 2021-01-19 04:46

    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't 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
    
    0 讨论(0)
  • 2021-01-19 04:47

    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.

    0 讨论(0)
提交回复
热议问题