用于实现`map`的`Data.Map`的模式匹配

时间:2014-06-11 02:04:13

标签: haskell

我正在研究如何实施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的每个值?

2 个答案:

答案 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)