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