使用平行運算計算多段文字中字母出現的頻率。
平行運算的重點,在於把那些也能循序完成的工作改成同時進行。 常見的例子就是計算字母出現的頻率。 請運用平行運算,計算由多段文字組成的陣列中,每個字母的總出現頻率。
任務是 Lean 中撰寫非同步程式碼的主要抽象機制。 它們很輕量,可以在另一個執行緒上平行執行,也可以在同一個執行緒上並行執行。
任務可以是純的,也可以是不純的。
純任務是用Task.spawn建立的,它接受一個純計算。
不純任務則是從IO計算建立的,例如使用IO.asTask,它會把IO α提升成IO monad 中的Task α。
在這個練習中,calculateFrequencies這個函式是 monadic 的,會回傳IO (Std.TreeMap Char Nat)。
因此可以透過IO.asTask使用不純任務,也能在回傳最終值之前建立純任務來進行中間計算。
身為函式語言,Lean 中大多數的值都是持久性的,也就是不可變的。 看似修改某個值的操作,實際上會產生一個帶有所要求變更的新值。 這表示記憶體通常可以在任務之間安全地共享,而不會產生資料競爭。
不過請注意,並非所有資料結構都同樣適合這種持久性的用法。
例如,更新Array中的單一元素通常需要複製整個陣列。
Std.HashSet和Std.HashMap也是如此。
為了讓這類資料結構更有效率,Lean 採用了參照計數。 只要某個值的參照是唯一的,就可以用破壞性的方式執行更新,避免不必要的複製。
其他資料結構,例如List、Std.TreeSet和Std.TreeMap,在設計上會在內部共享結構。
執行更新時,它們會重複使用沒有變動的節點,因此多個值可以共享這些節點的參照。
因此,修改結構中的某一部分通常不需要複製整個值。
這讓它們特別適合在任務之間共享。
每個測試的執行時間都會用IO.monoNanosNow以奈秒為單位測量,並顯示在結果旁邊。
你可以試試不同的做法,看看它們對執行效能有什麼影響吧。