首页 > 解决方案 > 为什么 Seq.map 只评估 5 个元素?

问题描述

这段代码:

[0..100]
|> Seq.map (fun i -> printfn "%A" i)

仅迭代 5 个元素:

> [0..100]
- |> Seq.map (fun i -> printfn "%A" i);;
0
1
2
3
4
val it : seq<unit> = seq [(); (); (); (); ...]

为什么?如何强制它评估所有元素?

标签: f#

解决方案


这发生在这里:表达式

[0..100] |> Seq.map (fun i -> printfn "%A" i)

计算成一系列单位,可视化为“()”。这不是您通常想要的。您可以看到地图内的函数通过 FSI 的(f# 交互)输出返回单位:

val it : seq<unit> = seq [(); (); (); (); ...]

如果你写,你会得到相同的结果

[0..100] |> Seq.map (fun _ -> ())

但与 (fun _ -> ()) 相比,函数 (fun i -> printfn "%A" i) 具有打印到控制台的副作用。这就是为什么您会在结果 [();之前看到打印的数字的原因。(); (); ()]

打印 5 个数字的原因是 FSI 内部:当它打印序列时,它们是可枚举的集合,它只枚举前几个。这里它枚举前5个元素并显示前4个,即[(); (); (); ()]

你真正想要的是一个接受一个值并返回一个新值的 map 函数,换句话说,一个将一个值映射到另一个值的函数:

[0..100] |> Seq.map (fun i -> i * 2)
val it : seq<int> = seq [0; 2; 4; 6; ...]

如您所见,这将返回序列的结果。同样,FSI 仅显示序列的前几个元素。如果将序列转换为列表,您会看到更多元素(但仍然不是全部,因为 100 太多了):

[0..100] |> Seq.map (fun i -> i * 2) |> Seq.toList 
val it : int list =
  [0; 2; 4; 6; 8; 10; 12; 14; 16; 18; 20; 22; 24; 26; 28; 30; 32; 34; 36; 38;
  40; 42; 44; 46; 48; 50; 52; 54; 56; 58; 60; 62; 64; 66; 68; 70; 72; 74; 76;
  78; 80; 82; 84; 86; 88; 90; 92; 94; 96; 98; 100; 102; 104; 106; 108; 110;
  112; 114; 116; 118; 120; 122; 124; 126; 128; 130; 132; 134; 136; 138; 140;
  142; 144; 146; 148; 150; 152; 154; 156; 158; 160; 162; 164; 166; 168; 170;
  172; 174; 176; 178; 180; 182; 184; 186; 188; 190; 192; 194; 196; 198; ...]

这个缩写来自FSI,它终止了表达式输出的长度。您可以使用 fsi.PrintLength、PrintWidth 等配置这些输出的长度。请参阅文档。

如果要枚举列表序列的所有元素,可以编写实用函数,例如

let plo xs  = xs |> Seq.iter (printfn "%O") 
[0..10] |> Seq.map (fun i -> i * 2) |> Seq.toList |> plo
> 
0
2
4
6
8
10
12
14
16
18
20
val it : unit = ()

推荐阅读