从“形状”的一种表示形式转换为另一种形式

时间:2019-01-03 23:27:04

标签: haskell

以下定义表示在网格上特定坐标处由彩色正方形组成的形状:

type AltShape = [Point]
data Point = P Colour (Int,Int)  deriving Eq
data Colour = Black | Red | Green | Blue  deriving Eq

我应该假设坐标始终为正,坐标(0,0)指向图片的左上角,并且y坐标向下增长

红色L形可以表示为

lShape = [P Red (0,0), P Red (0,1), P Red (0,2), P Red (1,2)]

表示这种形状的另一种方法是列表列表,每行一个列表:

type Shape = [Row] 
type Row   = [Square]
type Square = Maybe Colour

例如,上面的红色L形将由以下Shape类型的值表示:

lShape2 = [[x,o]
          ,[x,o]
          ,[x,x]] where x = Just Red
                        o = Nothing

我的任务是定义一个函数toShape :: AltShape -> Shape,该函数将从AltShape转换为Shape。我还有另一个任务来定义函数fromShape :: Shape -> AltShape,但其中data Shape = S [Row]在其中。我发现这很简单,并这样写:

    fromShape :: Shape -> AltShape
    fromShape (S rows) = [ P c (x,y) | (y,row) <- index rows, (x,Just c) <- index row]
     where index = zip [0..]

但是,我对此有更多麻烦。我首先创建函数

colourAndCoords :: Point -> (Colour,(Int,Int))
colourAndCoords ( P c (x,y) ) = (c,(x,y))

然后我创建了一个函数

coords :: [Point] -> [(Int,Int)]
coords ps = map snd (map colourAndCoords ps)

我的想法是将此列表与所有可能的协调的另一个列表进行比较,如果有匹配项,则添加正确的颜色,如果没有,则不添加任何内容。但是,我的老师说我太复杂了,应该考虑另一种解决方案。但是我很难想到一个。所以我想我的问题是更简单的方法是什么?我不是在寻求解决方案,只是在朝着正确的方向轻推。

感谢任何花时间阅读并回应的人!

如果我想出一个解决方案,我会回来并更新此线程。

2 个答案:

答案 0 :(得分:1)

如果您想要一个有效的解决方案,accumArray函数几乎可以完全满足您的需要(在计算了适当的界限之后)。

λ> arr = accumArray (const Just) Nothing ((0, 0), (2, 1)) [((y, x), c) | P c (x, y) <- lShape]
λ> arr
array ((0,0),(2,1)) [((0,0),Just Red),((0,1),Nothing),((1,0),Just Red),((1,1),Nothing),((2,0),Just Red),((2,1),Just Red)]
λ> elems arr
[Just Red,Nothing,Just Red,Nothing,Just Red,Just Red]

现在,问题已减少到split elements into groups

λ> chunksOf 2 (elems arr)
[[Just Red,Nothing],[Just Red,Nothing],[Just Red,Just Red]]

对于真正的应用程序,您可能希望将其保留为数组,因为数组索引快速( O (1))而列表索引速度很慢( O n ))。

答案 1 :(得分:0)

如果不关心效率,则可以考虑采用这种方法:

  1. 找出最大的x坐标(称为w)和最大的y坐标(称为h)。
  2. 创建一个矩形列表,长度为w x h,并具有列表理解或类似的含义。在每个位置上,通过点的整个列表查找具有匹配位置的点,如果找到一个,则使用其颜色(否则{Nothing)。

如果要关注效率,则可以考虑采用更复杂的方法:

  1. 将您的[AltShape]变成IntMap (IntMap Colour),例如使用this technique,它将y和x映射到颜色。
  2. 使用toAscList遍历占用的行;在每个列中,使用toAscList遍历占用的列。您将需要使用[]手动填充未占用的行,并使用Nothing手动填充未占用的列。

第二种方法的一个优点(或取决于您的目标,可能是不利的!)是它会自然产生“参差不齐”的Shape,而忽略了后面的Nothing