词典列表与词典序列的列表

时间:2018-10-24 20:47:07

标签: f#

在此示例中,我很难理解F#的List和Seq之间的区别。我以为主要的区别是Seq有点懒,但是我一定想念一些东西。

此代码段:

open System.Collections.Generic
let arr = 
  ["a"; "b"; "c"]
  |> Seq.map (fun a -> let dic = Dictionary () in dic.Add("key", a); dic) in
arr
|> Seq.iter (fun a -> 
  printfn "here";
  a.["key"] <- "something"
  );
arr
|> Seq.iter (fun a -> printfn "%s" a.["key"])

给予

here
here
here
a
b
c

位置(将第一个Seq替换为List)

open System.Collections.Generic
let arr = 
  ["a"; "b"; "c"]
  |> List.map (fun a -> let dic = Dictionary () in dic.Add("key", a); dic) in
arr
|> Seq.iter (fun a -> 
  a.["key"] <- "something"
  );
arr
|> Seq.iter (fun a -> printfn "%s" a.["key"])

给予

something
something
something

为什么当我使用Seq时词典值不会改变?在打印here时,显然可以访问这些元素。

谢谢。

2 个答案:

答案 0 :(得分:4)

原因恰恰是您所说的Seq是“有点懒”。

从每一次您要求它得到评估的意义上说,它都是“懒惰的”。所有的。直到最后一件非偷懒的事情。

尤其是,对Seq.map的调用是惰性的。它不会在充满字典的内存中创建新结构。相反,它创建了一些您可以称为“管道”的东西。该管道从您的列表["a"; "b"; "c"]开始,然后有一条指令:每当有人尝试对该序列进行迭代时,请为每个元素创建一个新字典。那里的“每次”位都很重要-因为您要遍历序列两次(一次打印“ here”,另一次打印值),所以字典也被创建两次。您将“ something”放入其中的词典与从中获得“ key”的词典不是同一词典。

为进一步说明,请尝试以下操作:

let s = ["a";"b";"c"] |> Seq.map( fun x -> printfn "got %s" x; x )
s |> Seq.iter(printfn "here's %s")
s |> Seq.iter(printfn "again %s")

这将打印以下内容:

got a
here's a
got b
here's b
got c
here's c
got a
again a
got b
again b
got c
again c

查看每个元素两次“获取”输出如何发生?这是因为Seq.map每次迭代都有效,而不仅仅是一次。


列表不一样。每次List.map时,都会在内存中创建一个全新的列表。它只是永远坐在那里(在其中“永远”被定义为“直到垃圾收集器到达它”)并等待您对其进行处理。如果您使用它做多件事,它仍然是相同的列表,不会重新创建它。这就是为什么您的词典始终是相同的词典,而不是像Seq中那样重新创建它们的原因。这就是为什么您可以修改它们并在下次查看时看到修改的原因。


Seq.cache的帮助下,您可以获得与序列相似但不太完全的效果。此函数采用常规的按需求值序列,并返回相同的序列,只是每个元素仅求值一次。

尽管与列表不同,Seq.cache不会在调用整个序列时对其求值。相反,它将创建一个可变的缓存,每次评估时都会更新。

这对于序列非常大甚至无限的情况很有用,但是您只需要在序列开始时使用少量的元素即可。

插图:

let s = ["a";"b";"c"] 
        |> Seq.map( fun x -> printfn "got %s" x; x ) 
        |> Seq.cache
s |> Seq.iter(printfn "here's %s")
s |> Seq.iter(printfn "again %s")

输出:

got a
here's a
got b
here's b
got c
here's c
again a
again b
again c

答案 1 :(得分:1)

我在两个示例中都添加了printfn,因此您可以看到不同之处:

let arr = 
    ["a"; "b"; "c"]
    |> Seq.map (fun a -> printfn "seq: %s" a
                         let dic = Dictionary ()
                         dic.Add("key", a)
                         dic)
arr
|> Seq.iter (fun a -> 
    printfn "here seq"
    a.["key"] <- "something"
)
arr
|> Seq.iter (fun a -> printfn "%s" a.["key"])

产生以下输出:

seq: a
here seq
seq: b
here seq
seq: c
here seq
seq: a
a
seq: b
b
seq: c
c

与此同时:

let arr = 
    ["a"; "b"; "c"]
    |> List.map (fun a -> printfn "list: %s" a
                          let dic = Dictionary ()
                          dic.Add("key", a)
                          dic)
arr
|> Seq.iter (fun a -> 
    printfn "here list";
    a.["key"] <- "something"
)
arr
|> Seq.iter (fun a -> printfn "%s" a.["key"])

产生以下输出:

list: a
list: b
list: c
here list
here list
here list
something
something
something

如您所见,行为是完全不同的。

Seq.map是惰性的,这意味着它仅在严格必要时才保留为以后调用的函数。每次调用它时,它都会从开始就根据需要映射每个元素开始。 Seq.map被调用两次,每个Seq.iter被调用,并且每次它为每个元素创建一个新的Dictionary时,该元素都会被垃圾收集器丢弃。

另一方面,List.map仅被调用一次,并且遍历整个输入列表,仅一次创建新的字典列表。