为什么不能选择Seq.tail [英] Why Seq.tail is not an option

查看:92
本文介绍了为什么不能选择Seq.tail的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题是输入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.taillistLen实现将具有二次复杂度(而不是列表的线性),因为每次调用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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆