这个Haskell函数是否在不必要地重新计算?

时间:2015-12-12 20:13:37

标签: haskell

我想计算无限整数网格中的值,其中每个正方形包含一个0到3的整数。我设计了一个增加正方形的函数:它将该正方形的值加1,但如果正方形会然后按住一个4,它将方块设置为0并在四个相邻的方块上调用自己。

type Board = (Int,Int) -> Int

setSquareTo :: Int -> (Int,Int) -> Board -> Board
setSquareTo n (x,y) b (z,w) | x == z && y == w = n
                            | otherwise        = b (z,w)

incSquare :: (Int,Int) -> Board -> Board
incSquare (x,y) b | b (x,y) == 3 = incSquare (x+1,y).incSquare (x,y+1).incSquare (x-1,y).incSquare (x,y-1).setSquareTo 0 (x,y) $ b
                  | otherwise    = setSquareTo (b (x,y) + 1) (x,y) b

现在,我认为这只是在incSquare生成的棋盘中找到一个平方的值时所需的计算。然而,似乎在一个接一个地找到两个相邻正方形的值会浪费大量时间在每种情况下进行一些相同的计算。这是真的吗?

如果是这样,有一种简单的方法可以提高性能吗?会像

type Board = IntMap (IntMap Int) 

使用严格版本的IntMap会更好吗? (在这种情况下,我如何将每个方块设置为0?)

2 个答案:

答案 0 :(得分:2)

这是我为data-inttrie撰写的那种东西。它的行为就像Int中的函数一样,只不过它通过memoization支持有效的单点修改。

您可以使用fmap f identity初始化任何函数f,然后使用overwritemodify一次更改一个值。

答案 1 :(得分:1)

使用Board类型的函数并不是一个好主意,因为当对setSquareTo进行更多调用时,函数变得越来越复杂。 然后将使用越来越多的空间来保存这个越来越复杂的功能。此外,当使用值(x, y)调用它并且长时间未调用setSquareTo时,需要很长时间,因为函数首先检查提供的参数是否是其他坐标之一

因此,您应该使用Map (Int, Int) Int或建议的IntMap (IntMap Int)代替Board的类型。

如果您使用Map (Int, Int) Int,则在这种情况下,您可以使用findWithDefault (x, y) 0 b代替b (x, y),如果地图中尚不存在此值,则可以获取0,因此您不需要必须首先将每个值设置为0,这在您拥有无限板时是不可能的。