为地牢地图选择什么数据类型

时间:2013-10-20 20:02:07

标签: haskell data-structures

作为编码挑战的一部分,我必须实施地下城地图。

我已经使用Data.Map作为设计选择进行设计,因为不需要打印地图,有时我不得不更新地图图块,例如当一个障碍被摧毁时。

type Dungeon = Map Pos Tile

type Pos = (Int,Int) -- cartesian coordinates
data Tile = Wall | Destroyable | ...

但是,如果我必须打印它,那么我将不得不使用类似的东西 elaboratePrint . sort $ fromList dungeon其中elaboratePrint处理换行符并从tileset中生成漂亮的unicode符号。

我考虑的另一个选择是嵌套列表

type Dungeon = [[Tile]]

这样做的缺点是,很难更新这种数据结构中的单个元素。但是,然后打印将是一个简单的衬垫unlines . map show

我考虑的另一个结构是Array,但由于我不习惯对hackage文档进行简短的一瞥 - 我只发现了一个对索引进行操作的map函数和一个对元素进行操作的函数,除非是愿意使用可变数组更新一个元素乍一看并不容易。打印阵列也不清楚如何快速轻松地完成这项工作。

所以现在我的问题 - 是否有更好的数据结构来表示具有易于打印和轻松更新单个元素的属性的地下城地图。

2 个答案:

答案 0 :(得分:4)

Array怎么样? Haskell有真正的二维数组。

import Data.Array.IArray -- Immutable Arrays

现在ArrayIx a => a编入索引。幸运的是,有一个实例(Ix a, Ix b) => Ix (a, b)。所以我们可以

 type Dungeon = Array (Integer, Integer) Tile

现在用几个函数中的任何一个构造其中一个函数,最简单的就是

array :: Ix i => (i, i) -> [(i, a)] -> Array i a

所以对你来说,

startDungeon = array ( (0, 0), (100, 100) )
              [ ( (x, y), Empty ) | x <- [0..100], y <- [0..100]]

只需将100Empty替换为适当的值。

如果速度成为一个问题,那么使用MArrayST是一个简单的解决方法。我建议不要切换,除非速度实际上是一个真正的问题。

解决漂亮的印刷问题

import Data.List
import Data.Function

pretty :: Array (Integer, Integer) Tile -> String
pretty = unlines . map show . groupBy ((==) `on` snd.fst) . assoc

然后可以将map show设置为您想要将[Tile]格式化为一行。如果你确定你真的希望以一种非常有效的方式打印它们(可能是控制台游戏),你应该看一个合适的漂亮的打印库,比如this一个。

答案 1 :(得分:3)

首先 - 像Data.Map这样的树喜欢和列表仍然是函数式语言的自然数据结构。如果你只需要矩形地图,那么Map在结构方面有点过分,但[[Tile]]实际上可能非常好。对于随机访问和更新都有O(√n),这不是太糟糕。

特别是,比2D数组(O(n)的纯功能更新更好!因此,如果您需要非常好的性能,则无法使用可变数组。虽然游戏本质上与IO和状态有关,但这并不一定是坏事。正如jozefg所指出的,{em> 对Data.Array的好处是能够将元组用作Ix索引,因此我会使用MArray

阵列打印很容易。你可能只需要整个地图的矩形部分,所以我只是用一个简单的列表理解来提取这样的切片

[ [ arrayMap ! (x,y) | x<-[21..38] ] | y<-[37..47] ]

您已经知道如何打印列表。