将地图视为有限函数的表示,可以以咖喱或非曲面的形式给出两个或多个变量的映射;也就是说,Map (a,b) c
和Map a (Map b c)
类型是同构的,或与它接近的东西。
有哪些实际考虑 - 效率等 - 用于在两种表示之间进行选择?
答案 0 :(得分:17)
元组的Ord
实例使用字典顺序,因此Map (a, b) c
首先会按a
排序,因此整体顺序将相同。关于实际考虑:
因为Data.Map
是一个二进制搜索树,所以在一个键上的分割与查找相当,因此在未处理的表单中获取给定a
的子图将不会更加昂贵而不是以咖喱形式。
咖喱形式可能会产生一个不太平衡的树,因为有多棵树而不是一棵树的明显原因。
咖喱表格会有一些额外的开销来存储嵌套地图。
如果某些a
值产生相同的结果,则可以共享表示“部分应用程序”的咖喱表格的嵌套地图。
同样,咖喱表格的“部分申请”会为您提供现有的内部地图,而未表格的表格必须构建新地图。
因此,未经证实的表格明显更好,但如果您希望经常进行“部分申请”并且可以从共享Map b c
值中受益,那么咖喱表格可能会更好。
请注意,必须小心谨慎,以确保您从潜在的共享中实际受益;您需要显式定义任何共享内部映射,并在构造完整映射时重用单个值。
编辑: Tikhon Jelvis在评论中指出,元组构造函数的内存开销 - 我认为没有考虑到 - 根本不是微不足道的。咖喱形式肯定会有一些开销,但这个开销与有多少不同的a
值成正比。另一方面,未处理形式的元组构造函数开销与密钥总数成比例。
因此,如果平均而言,对于a
的任何给定值,有三个或更多使用它的不同键,您可能会使用咖喱版本来节省内存。当然,对不平衡树木的担忧仍然适用。我想的越多,我就越怀疑咖喱的形式是明确的更好,除非你的钥匙非常稀疏且分布不均匀。
请注意,由于定义的确定对GHC很重要,因此如果要共享子表达式,则在定义函数时需要同样小心;这是您有时看到以这样的样式定义函数的一个原因:
foo x = go
where z = expensiveComputation x
go y = doStuff y z
答案 1 :(得分:4)
元组在两个元素中都是懒惰的,所以元组版本引入了一些额外的懒惰。这是好还是坏很大程度上取决于您的使用情况。 (特别是,比较可能会强制元组元素,但前提是有许多重复的a
值。)
除此之外,我认为这将取决于你有多少重复。如果a
几乎总是不同b
,那么你将会有很多小树,所以元组版本可能会更好。另一方面,如果情况相反,非元组版本可能会为您节省一点时间(一旦找到合适的子树并且您正在寻找a
,就不会经常重新比较b
)。
我想起了尝试,以及它们如何存储公共前缀一次。非元组版本似乎有点像。如果有很多共同的前缀,trie可能比BST更有效,如果没有,则效率更低。
但最重要的是:基准测试!! ; - )
答案 2 :(得分:3)
除了效率方面,这个问题还有一个务实的方面:你想用这个结构做什么?
例如,您是否希望能够为类型a
的给定值存储空地图?如果是这样,那么未经证实的版本可能更实用!
这是一个简单的例子:假设我们想要存储String
- 值的人物属性 - 比如该人的stackoverflow个人资料页面上某些字段的值。
type Person = String
type Property = String
uncurriedMap :: Map Person (Map Property String)
uncurriedMap = fromList [
("yatima2975", fromList [("location","Utrecht"),("age","37")]),
("PLL", fromList []) ]
curriedMap :: Map (Person,Property) String
curriedMap = fromList [
(("yatima2975","location"), "Utrecht"),
(("yatima2975","age"), "37") ]
使用curried版本,没有很好的方法来记录系统已知用户"PLL"
但未填写任何信息的事实。人/属性对("PLL",undefined)
将导致运行时崩溃,因为密钥中的Map
是严格的。
您可以将curriedMap
的类型更改为Map (Person,Property) (Maybe String)
并将Nothing
存储在那里,这可能是此案例中的最佳解决方案;但是,如果存在未知/不同数量的属性(例如,取决于人的类型),那么也会遇到困难。
所以,我想这也取决于你是否需要这样的查询功能:
data QueryResult = PersonUnknown | PropertyUnknownForPerson | Value String
query :: Person -> Property -> Map (Person, Property) String -> QueryResult
在咖喱版中很难写(如果不是不可能的话),但在未经证实的版本中很容易。