为什么不能选择Seq.tail [英] Why Seq.tail is not an option
问题描述
我的问题是输入Seq.
时为什么没有Seq.tail
功能?
My question is when entering Seq.
why is there no Seq.tail
function?
在此不将序列转换为列表的代码中,递归函数中没有可用的Seq.tail
函数.是因为使用Seq.initInfinte
来创建序列,还是还有其他原因?
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
但是,这很好.我对为什么Seq.tail
不可用,但List.tail
可用的原因感到困惑.
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
推荐答案
没有特定的原因,就是从未添加过.尽管它在F#的更高版本中可用(为在4.0中对集合API进行正则化做出了很大努力).
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).
但是,人们可以提供一个常识性的论据,说明为什么Seq.tail
会显得微不足道,甚至可能是危险的.可能实际上是最初不添加它的原因,但我不确定.
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.
列表是一个具有两个字段的数据结构:第一个元素(称为"head"),其余元素本身仅是另一个列表(称为"tail").因此,调用List.tail
意味着仅采用数据结构的第二个字段.无需复杂的处理,只需采用数据结构的其中一个字段即可.
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.
序列基本上是一个函数(称为IEnumerable.GetEnumerator
),该函数返回可变数据结构(称为IEnumerator
),该数据结构可以被反复踢"(通过调用IEnumerator.MoveNext
),从而产生每次踢下一个项目,并更改其内部状态.
此代表表示,为了删除"序列的第一个元素,一个人必须采用原始序列并将其包装在另一个函数中,当要求生成IEnumerator
时,该函数将获得内部序列的IEnumerator
,然后踢一次,然后返回到呼叫者.遵循以下原则(伪代码):
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
这意味着,尽管有了列表,每次对tail
的调用都会使数据结构更少复杂(少一个项目,只剩下尾部),而对每次tail
的调用都将按顺序进行它 more 复杂(一个功能包装器).而且,如果连续执行几次tail
序列,然后对结果进行迭代,即使在逻辑上看起来像整个原始序列,您仍将对其进行迭代对你来说更短.
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.
将其应用于您的特定情况,基于Seq.tail
的listLen
实现将具有二次复杂度(而不是列表的线性),因为每次调用Seq.isEmpty
时,都会有效地导致迭代达到第一个未跳过的项目,对listLen
的每个递归调用都会添加另一个跳过的项目以进行迭代.
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.
对于它的价值,标准的.NET LINQ实际上具有等效的操作-称为.Skip
,您可以从F#中完全使用它:
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)
或者,正如罗伯特·尼尔森(Robert Nielsen)在评论中指出的那样,实际上甚至在F#标准库中也有一个Seq.skip
(我是用手机编写的,当时无法验证):
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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!