使用并行计算统计文本中字母的频率。
并行是指把那些同样可以顺序完成的事情并行地做。一个常见的例子就是统计字母的频率。利用并行计算来算出文本数组中每个字母的总频率。
任务是在 Lean 中编写异步代码的主要抽象。 它们很轻量,可以在另一个线程上并行执行,也可以在同一线程上并发执行。
任务可以是纯的,也可以是非纯的。
纯任务用Task.spawn创建,它接受一个纯计算。
非纯任务则由IO计算创建,例如使用IO.asTask,它会在IO单子中把IO α提升为Task α。
在本练习中,函数calculateFrequencies是单子式的,返回一个IO (Std.TreeMap Char Nat)。
这样就可以通过IO.asTask使用非纯任务,也可以在返回最终值之前为中间计算生成纯任务。
作为一种函数式语言,Lean 中的大多数值都是持久化的,也就是不可变的。 看似修改某个值的操作,实际上会生成一个包含所需改动的新值。 这意味着内存通常可以在任务之间安全地共享,而不会引入数据竞争。
不过要注意,并非所有数据结构都同样适合持久化使用。
例如,更新Array中的单个元素通常需要复制整个数组。
Std.HashSet和Std.HashMap也是如此。
为了让这类数据结构保持高效,Lean 使用了引用计数。 只要某个值拥有唯一的引用,更新就可以破坏性地执行,避免不必要的复制。
其他数据结构,比如List、Std.TreeSet和Std.TreeMap,则在设计上支持内部结构共享。
执行更新时它们会复用未发生变化的节点,因此多个值可以共享对这些节点的引用。
这样一来,修改结构的某一部分通常不需要复制整个值。
这使得它们特别适合在任务之间共享。
每个测试的执行时间都用IO.monoNanosNow以纳秒为单位测量,并显示在结果的旁边。
你可以尝试不同的做法,看看它们对运行时性能有什么影响。