在此示例中,我很难理解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
时,显然可以访问这些元素。
谢谢。
答案 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
仅被调用一次,并且遍历整个输入列表,仅一次创建新的字典列表。