2

作为编码挑战的一部分,我必须实现一个地牢地图。

我已经将它Data.Map设计为设计选择,因为不需要打印地图,有时我必须更新地图图块,例如当障碍物被破坏时。

type Dungeon = Map Pos Tile

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

但是如果我也必须打印它怎么办 - 那么我将不得不使用 elaboratePrint . sort $ fromList dungeonwhereelaboratePrint处理换行符并从图块集中制作漂亮的 unicode 符号之类的东西。

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

type Dungeon = [[Tile]]

这将有一个缺点,即很难更新这种数据结构中的单个元素。但印刷将是一个简单的单行unlines . map show

我考虑过的另一种结构是Array,但由于我不习惯数组,因此我只看了一下 hackage 文档——我只发现了一个对索引进行操作的 map 函数和一个对元素起作用的函数,除非有人愿意使用可变数组更新一个乍一看,元素并不容易。并且打印数组也不清楚如何快速轻松地做到这一点。

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

4

2 回答 2

4

一个怎么样Array?Haskell 有真实的二维数组。

import Data.Array.IArray -- Immutable Arrays

现在 anArray由 any 索引Ix 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]]

只需用100和替换Empty适当的值。

如果速度成为问题,那么使用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]为一行的任何格式。如果您决定真的希望以一种很棒且高效的方式打印这些内容(可能是控制台游戏),您应该查看一个合适的漂亮打印库,例如这个

于 2013-10-20T20:09:10.050 回答
3

首先——诸如Data.Map和列表之类的树状结构仍然是函数式语言的自然数据结构。Map如果您只需要矩形地图,在结构上有点过分,但[[Tile]]实际上可能很好。它具有O(√n)随机访问和更新功能,这还不错。

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

使用数组打印很容易。您可能只需要整个地图的矩形部分,所以我只需使用简单的列表理解来提取这些切片

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

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

于 2013-10-20T20:11:51.960 回答