是否有更通用的方法在F#中对集合进行迭代,过滤和应用操作?

时间:2011-10-02 13:36:10

标签: f#

我们来看看这段代码:

open System
open System.IO

let lines = seq {
  use sr = new StreamReader(@"d:\a.h")
  while not sr.EndOfStream do yield sr.ReadLine()
}

lines |> Seq.iter Console.WriteLine
Console.ReadLine()

在这里,我正在阅读seq中的所有行,并且要查看它,我正在使用Seq.iter。如果我有一个列表,我将使用List.iter,如果我有一个数组,我将使用Array.iter。是不是我可以使用更通用的遍历功能,而不是必须跟踪我有什么样的集合?例如,在Scala中,我只会调用foreach,无论我使用的是List,Array还是Seq,它都能正常工作。

我做错了吗?

3 个答案:

答案 0 :(得分:5)

根据您的具体情况,您可能需要或不需要跟踪您处理的收藏类型。

如果对项目进行简单的迭代,没有什么可能阻止您在F#中的列表或数组上使用Seq.iter:它将在数组和列表上工作,因为它们也是序列,或IEnumerable从.NET的角度来看。在数组上使用Array.iter或在列表上使用List.iter只会根据每种类型的集合的特定属性提供更有效的遍历实现。由于Seq.iter Seq.iter : ('T -> unit) -> seq<'T> -> unit的签名显示您在遍历后不关心您的类型'T

在其他情况下,如果您关心进一步的构图,您可能需要考虑输入和输出参数的类型并使用专门的函数。例如,如果您需要过滤列表并继续使用结果,那么

List.filter : ('T -> bool) -> 'T list -> 'T list会保留底层集合的完整类型,但是Seq.filter : ('T -> bool) -> seq<'T> -> seq<'T>应用于列表会返回一个序列,而不再是列表:

let alist = [1;2;3;4] |> List.filter (fun x -> x%2 = 0) // alist is still a list
let aseq = [1;2;3;4] |> Seq.filter (fun x -> x%2 = 0) // aseq is not a list anymore

答案 1 :(得分:4)

Seq.iter也适用于列表和数组。

类型seq实际上是接口IEnumerable<'T>的别名,listarray都是实现的。因此,正如BLUEPIXY指出的那样,您可以在数组或列表上使用Seq.*函数。

功能较少的方式如下:

for x in [1..10] do
    printfn "%A" x

答案 2 :(得分:2)

列表和数组被视为Seq。

let printAll seqData =
  seqData |> Seq.iter (printfn "%A")
  Console.ReadLine() |> ignore

printAll lines
printAll [1..10]
printAll [|1..10|]