为什么Seq.tail不是一个选择

时间:2017-03-20 19:15:45

标签: list visual-studio-2015 f# seq

我的问题是在输入Seq.时为什么没有Seq.tail功能?

在不将序列转换为列表的代码中,递归函数中没有Seq.tail函数。是因为Seq.initInfinte用于创建序列,还是有其他原因?

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

然而,这很好用。我很困惑为什么Seq.tail不可用,但List.tail可用。

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

2 个答案:

答案 0 :(得分:6)

没有具体原因,它只是从未被添加,就是这样。虽然它可以在更高版本的F#中使用(在4.0中规范集合API的工作量很大)。

然而,人们可以提供一个常识论证,为什么Seq.tail稍微有用,甚至可能是危险的。这实际上可能是最初没有添加它的原因,但我不确定。

你看,列表和序列在幕后有不同的表现形式。

List是一个包含两个字段的数据结构:第一个元素(称为“head”)和其余元素,它本身只是另一个列表(称为“tail”)。所以调用List.tail意味着仅仅采用数据结构的第二个字段。没有复杂的处理,只需要采用其中一个数据结构字段。

另一方面,序列基本上是一个函数(称为IEnumerable.GetEnumerator),它返回一个可变数据结构(称为IEnumerator),可以反复“踢”(通过调用{{1每次踢球时产生下一个项目,并改变其内部状态 这个表示意味着,为了“删除”序列的第一个元素,必须采用原始序列并将其包装在另一个函数中,当被要求生成IEnumerator.MoveNext时,将获得内部序列序列的IEnumerator,然后踢一次,然后返回给调用者。沿着这些方向的东西(伪代码):

IEnumerator

这意味着,对于列表,每次调用Tail(inner).GetEnumerator = let innerE = inner.GetEnumerator() innerE.MoveNext() innerE 都会使数据结构更少复杂(少一个项目,只剩下尾部),每次调用{{1会使它更多复杂(另外一个函数包装器)。更重要的是,如果你连续几次取一个tail的序列,然后迭代结果,你仍然会迭代整个原始序列,即使在逻辑上它看起来更短。

将此问题应用于您的具体案例,基于tail的{​​{1}}实施将具有二次复杂性(而不是列表的线性),因为每次调用tail时,都会有效地导致迭代到第一个非跳过的项目,每次递归调用listLen将添加另一个跳过的项目进行迭代。

对于它的价值,标准的.NET LINQ实际上有一个等效的操作 - 名为Seq.tail,你可以完全使用它来自F#:

Seq.isEmpty

或者,正如罗伯特·尼尔森在评论中指出的那样,即使在F#标准库中也是listLen(我用手机写的,当时无法验证):

.Skip

答案 1 :(得分:6)

F#3.x中没有Seq.tail。它在F#4.x中可用。我确认您的代码在4.0中编译,但不是3.1。

在F#4.0中添加了许多功能,以及#34;规范&#34; List,Array和Seq。

https://github.com/fsharp/fslang-design/blob/master/FSharp-4.0/ListSeqArrayAdditions.md

(我注意到Option在这个问题上被遗漏了,但怀疑它在某些时候也有更多的功能。)

至于为什么缺少所有这些功能,他们根本没有时间填补早期版本中的空白。