Haskell - 如何迭代并进行比较?

3

我有一个问题。我正在学习Haskell,并尝试构建地牢游戏。

目前,我正在尝试为玩家实现移动功能。在移动到新位置之前,我必须检查玩家的新位置是否会与游戏地图上的对象位置发生碰撞。

我在Java/C中想到的代码非常简单,但我不知道如何将其转换为Haskell。我很确定使用Haskell代码有一种替代方法,但这是一个大致的想法(Java/C):

假设玩家对象具有x和y坐标。 还假设我们在数组中存储了玩家可能与之碰撞的其他对象的列表(我认为这将存储在Haskell的列表中)。另外,让我们假设每个对象都有x和y坐标。 如果发生碰撞,布尔函数返回true,否则返回false

   Boolean detectCollision(Player p, Object[] o)
   {
       for(int i=0; i < o.length; i++){
           if(p.x==o[i].x && p.y==o[i].y){
               return true;
           }
       } return false; 
   }

如果有人能帮我弄清楚这个问题,我将非常感激。

在命令式语言(如C/Java)中使用迭代的操作,在Haskell中通常使用递归来完成,可以通过在函数中显式地定义或使用现有的能力(如fold、map或reduce)来实现。 - Greg
1个回答

7
即使这是Java/C,我也建议您编写一个函数来检测玩家和单个对象是否会发生碰撞,因此让我们在这里做到这一点:
collidesWith :: Player -> Object -> Bool
collidesWith player object =
    playerX player == objectX object && playerY player == objectY object

这段文字有些啰嗦,您可以使用 lens 库来缩短它,使其看起来更加简洁明了。

collidesWith player object
    = player^.x == object^.x && player^.y == object^.y

但这超出了本问题的范围,只需知道在Haskell中完全可以实现。

要在Haskell中“循环”列表,可以使用递归。

detectCollision player [] = False  -- No objects, no collisions
detectCollision player (o:objects)
    = player `collidesWith` o || detectCollision player objects

由于Haskell是惰性的,第一次player `collidesWith` o评估为True时,它将停止检查。但实际上已经存在一个名为any的标准内置函数:

any :: (a -> Bool) -> [a] -> Bool

可以被用作

detectCollision player objects = any (collidesWith player) objects

Haskell甚至允许通过eta缩减省略objects参数,因此可以简单地编写如下:

detectCollision :: Player -> [Object] -> Bool
detectCollision player = any (collidesWith player)

这就是全部内容!

注意:假设PlayerObject已经定义好了。

data Player = Player
    { playerX :: Int
    , playerY :: Int
    } deriving (Eq, Show)

data Object = Object
    { objectX :: Int
    , objectY :: Int
    } deriving (Eq, Show)

顺带一提,这超出了问题的范围,我喜欢使用线性包中的Point来表示位置。然后你可以比较player^.position == object^.position并为每个提供一个“委托”的R2实例,这样你也可以说player^._xobject^._y - Rein Henrichs
值得一提的是,Haskell 是惰性求值的,因此 any 函数在第一次匹配成功后就会停止运行。 - mb14
1
@mb14 编辑后包含该注释 - bheklilr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接