oct*_*bus 3 f# list seq visual-studio-2015
我的问题是什么时候输入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
Run Code Online (Sandbox Code Playgroud)
但是,这很好用.我很困惑为什么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
Run Code Online (Sandbox Code Playgroud)
没有具体的原因,它只是从未被添加,就是这样.虽然它可以在更高版本的F#中使用(但是在4.0中规范化集合API方面付出了巨大的努力).
然而,人们可以提供一个常识性的论据,说明为什么Seq.tail会有一点点用处,甚至可能是危险的.这实际上可能是最初没有添加它的原因,但我不确定.
你看,列表和序列在幕后有不同的表现形式.
List是一个包含两个字段的数据结构:第一个元素(称为"head")和其余元素,它本身只是另一个列表(称为"tail").因此,调用List.tail仅意味着采用数据结构的第二个字段.没有复杂的处理,只需要采用其中一个数据结构字段.
另一方面,序列基本上是一个函数(被称为IEnumerable.GetEnumerator),它返回一个可变数据结构(被称为IEnumerator),可以反复"踢"(通过调用IEnumerator.MoveNext),在每次踢动时产生下一个项目,并改变其内部状态.
这个表示意味着,为了"删除"序列的第一个元素,必须采用原始序列并将其包装在另一个函数中,当被要求生成一个函数时IEnumerator,它将得到内部序列IEnumerator,然后踢它一次,然后返回呼叫者.沿着这些方向的东西(伪代码):
Tail(inner).GetEnumerator =
let innerE = inner.GetEnumerator()
innerE.MoveNext()
innerE
Run Code Online (Sandbox Code Playgroud)
这意味着,虽然每个调用都有一个列表,但是tail使得数据结构不那么复杂(少一个项目,只剩下尾部),每次调用序列tail都会使它更复杂(一个函数包装器).更重要的是,如果你tail连续多次获取一个序列,然后迭代结果,你仍然会迭代整个原始序列,即使逻辑上它看起来更短.
将此应用于您的特定情况,您的listLen实现Seq.tail将具有二次复杂性(而不是列表的线性),因为每次调用时Seq.isEmpty,这将有效地导致迭代到第一个非跳过的项,并且每次递归调用listLen将添加另一个跳过的项目进行迭代.
对于它的价值,标准的.NET LINQ实际上有一个等效的操作 - 称为.Skip,你可以完全使用它来自F#:
open System.Linq
let seqTail (s: _ seq) = s.Skip(1)
Run Code Online (Sandbox Code Playgroud)
或者,正如Robert Nielsen在评论中指出的那样,它实际上是一个Seq.skipF#标准库(我用手机写的,当时无法验证):
let seqTail s = Seq.skip 1 s
Run Code Online (Sandbox Code Playgroud)
Seq.tail不适用于F#3.x. 它在F#4.x中可用.我验证您的代码编译在4.0,但不是3.1.
在F#4.0中添加了许多功能,以"规范化"List,Array和Seq.
https://github.com/fsharp/fslang-design/blob/master/FSharp-4.0/ListSeqArrayAdditions.md
(我注意到Option在这个问题上被遗漏了,但是怀疑它在某些时候也有更多的功能.)
至于为什么缺少所有这些功能,他们根本没有时间填补早期版本中的这些空白.