我正在研究如何实施Data.Map#map
将函数f
应用于每个Data.Map
的值。
让我们在map'
上考虑Haskell中的List
:
map' :: [a] -> (a -> b) -> [b]
map' [] _ = []
map' (x:xs) f = f x : map' xs f
请注意,我可以在(x:xs)
上进行模式匹配,以递归方式调用map' xs f
。
如何模式匹配以递归方式将f
应用于Data.Map
的每个值?
答案 0 :(得分:14)
虽然你不能自己编写,因为Data.Map不会导出构造函数,但我们可以作弊。
具体来说,如果我们在github上提取来源,我们可以稍微探讨一下fmap
是如何实现的。
Map
的实际定义非常简单
-- Ignore the !'s and UNPACK, it's telling GHC to do some clever things
-- with how strict the constructors are.
data Map k a = Bin {-# UNPACK #-} !Size !k a !(Map k a) !(Map k a)
| Tip
因此Map
为空,Tip
或分支Bin
,具有键,值,大小和两个子子。
Size
的定义非常简单:)
type Size = Int
现在,转到Functor
实例!准确的定义是
instance Functor (Map k) where
fmap f m = map f m
现在map
正如您所希望的那样
map :: (a -> b) -> Map k a -> Map k b
map _ Tip = Tip
map f (Bin sx kx x l r) = Bin sx kx (f x) (map f l) (map f r)
我们所做的就是用f
修改值,然后递归。琐事:)
平衡和其他tomfoolery不需要在这里发生,因为我们只修改树中的值,而不是它所订购的结构或键。
现在,如果你没有编写一些代码,它就会解决问题,那么为什么不为类似的,更简单的结构实现fmap
data BinTree a = Node a (BinTree a) (BinTree a)
| Leaf
答案 1 :(得分:2)
@ jozefg的回答非常好。去做。但是每隔一段时间你真的想要打破数据抽象,例如Data.Map.Map
,这实际上是可能的。您可以使用Template Haskell访问模块的内部,lens
包实际上提供了完成它所需的一切。我没有评论就提出这个问题,因为大多数时候这不是你应该做的事情。
{-# LANGUAGE TemplateHaskell #-}
module M where
import Data.Map as Map
import Control.Lens.TH
import Control.Lens
$(makePrisms ''Map)
mymap :: (a -> b) -> Map k a -> Map k b
mymap f inmap
| Just () <- inmap ^? _Tip = review _Tip ()
| Just (sz,k,a,l,r) <- inmap ^? _Bin =
review _Bin (sz,k, f a, mymap f l, mymap f r)