您不能修改Map
就位(因为 Haskell 是一种纯函数式语言),但您可以创建一个几乎与旧映射相同的新映射,除了一些已修改的条目之外。
(不要太担心效率:与直觉相反,新的Map
不需要旧副本的完整副本。)
例如,假设我们要计算字符串中每个字符的频率。让我们编写一个函数,给定一个 charc
,增加存储在内部的计数Map
import qualified Data.Map.Strict as M
countChar :: Char -> M.Map Char Int -> M.Map Char Int
countChar c oldMap = newMap
where
newMap = M.insertWith (+) c 1 oldMap
The newMap
不需要变量,为了清楚起见,上面显示了它。
功能insertWith
制作新地图,以便在索引处c
如果旧地图中没有值,则存储 1,或者1 + x
如果有先前的值x
在旧地图中。
为了处理完整的字符串,我们使用递归:
countString :: String -> M.Map Char Int
countString "" = M.empty
countString (c:cs) = countChar c (countString cs)
GHCi 中的小测试:
> countString "here's an example"
fromList [(' ',2),('\'',1),('a',2),('e',4),('h',1),('l',1),('m',1)
,('n',1),('p',1),('r',1),('s',1),('x',1)]
对于更高级的解决方案,countString
如果需要的话,也可以重写为折叠。使用左严格折叠也会提高效率。
countString = foldl' (flip countChar) M.empty
人们甚至可以使用状态单子来避免传递Map
。如果您正在学习 Haskell,请不要担心这一点,首先学习如何使用递归、模式匹配和一些库函数来解决此类任务Map
s.