实现字典的适用实例地图,关联数组

问题描述

为关联数组实现函子实例(本质上是映射操作)似乎很简单(例如,参见Functor定义[1])。但是,applicative实例未定义。从理论上讲,地图不是适用语言吗?要成为应用程序,还需要哪些其他限制?

[1] https://hackage.haskell.org/package/containers-0.6.3.1/docs/Data-Map-Strict.html

解决方法

正如人们在评论中指出的那样,您不能为Applicative实现有效的Map实例,因为您不能以守法的方式实现pure。由于pure id <*> v = v的身份定律,pure实现需要在将地图与函数应用程序相交的同时维护所有键。您无法对部分地图执行此操作,因为根据参数,您可能在一个或另一个地图中都没有键来从中调出需要生成一个函数的功能a -> b或参数a b在结果映射中。 pure x需要像ZipList(使用repeat)那样工作,生成一个映射,该映射将每个键映射到相同的值{{1} },但是x是不可能的,因为它是有限的。但是, 可以使用允许无限映射的替代表示形式,例如基于函数和Map的映射。

Eq

不幸的是,这不只是语义上的无限:当您添加或删除键值对时,它在内存中也会无限增长 !这是因为条目是闭包的链接列表,而不是经过验证的数据结构:您只能通过添加指示其删除的条目从映射中删除值,例如版本控制系统中的还原。查找的效率也很低,因为键的数量是线性的,而不是-- Represent a map by its lookup function. newtype EqMap k v = EM (k -> Maybe v) -- Empty: map every key to ‘Nothing’. emEmpty :: EqMap k v emEmpty = EM (const Nothing) -- Singleton: map the given key to ‘Just’ the given value,-- and all other keys to ‘Nothing’. emSingleton :: (Eq k) => k -> v -> EqMap k v emSingleton k v = EM (\ k' -> if k == k' then Just v else Nothing) -- Insertion: add an entry that overrides any earlier entry -- for the same key to return ‘Just’ a new value. emInsert :: (Eq k) => k -> v -> EqMap k v -> EqMap k v emInsert k v (EM e) = EM (\ k' -> if k == k' then Just v else e k') -- Deletion: add an entry that overrides any earlier entry -- for the same key to return ‘Nothing’. emDelete :: (Eq k) => k -> EqMap k v -> EqMap k v emDelete k (EM e) = EM (\ k' -> if k == k' then Nothing else e k') emLookup :: EqMap k v -> k -> Maybe v emLookup (EM e) = e instance Functor (EqMap k) where -- Map over the return value of the lookup function. fmap :: (a -> b) -> EqMap k a -> EqMap k v fmap f (EM e) = EM (fmap (fmap f) e) instance Applicative (EqMap k) where -- Map all keys to a constant value. pure :: a -> EqMap k a pure x = EM (const (Just x)) -- Intersect two maps with application. (<*>) :: EqMap k (a -> b) -> EqMap k a -> EqMap k b fs <*> xs = EM (\ k -> emLookup k fs <*> emLookup k xs) 的对数。充其量,对于初学者中级的函数式程序员来说,这是一个不错的学术练习,只是为了了解如何用函数表示事物。

这里有一个简单的替代方法是“默认映射”,它将不存在的键映射到一个恒定值。

Map

然后data DefaultMap k v = DM v (Map k v) dmLookup :: (Ord k) => k -> DefaultMap k v -> v dmLookup k (DM d m) = fromMaybe d (Map.lookup k m) -- … 的实现很简单:现有键的交集以及默认情况下应用的不存在的键。

Applicative

instance Functor (DefaultMap k) where -- Map over the return value of the lookup function. fmap :: (a -> b) -> DefaultMap k a -> DefaultMap k b fmap f (DM d m) = DM (f d) (fmap f m) instance Applicative (DefaultMap k) where -- Map all keys to a constant value. pure x = DM x mempty -- Intersect two maps with application,accounting for defaults. DM df fs <*> DM dx xs = DM (df dx) $ Map.unions [ Map.intersectionWith ($) fs xs,fmap ($ dx) fs,fmap (df $) xs ] 有点不寻常,因为您可以可以删除键-值对,但是只有通过有效地将它们“重置”为默认值,才能查找给定的键即使删除了相同的密钥,也总是成功。尽管您当然可以使用DefaultMap来恢复类似于Map的部分行为,但默认值为DefaultMap k (Maybe v),并且始终将定义的键始终映射到Nothing

认为还有一个Just,与instance Monad (DefaultMap k)instance Monad ((->) k)同构,因为像instance Monad (Stream k)一样,Stream总是 无限,而可能有限的DefaultMap不能拥有ZipList实例,因为它必然违反了关联律Monad = a >=> (b >=> c)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...