Haskell:两个不同的函数使用相同的where子句

时间:2014-10-31 14:14:11

标签: performance function haskell

有没有办法可以使用相同的where子句为2个不同的函数创建一个结构?

我的代码:

bonusColBullet :: Bonus -> Bullet -> World -> World
bonusColBullet bn@(Bonus{bnpos=pos}) b w@(World{bullets=bs, bonuses=bns, score=s})
    | doBoxesCollide bnlp bnrp blp brp = w{bullets=delete b bs, bonuses=delete bn bns, score=incVal s}
    | otherwise = w
    where
        blp = bpos' - bSizeH --bullet corners
        brp = bpos' + bSizeH
        bnlp = pos - bnSizeH --bonus obj corners
        bnrp = pos + bnSizeH
        bpos' = bpos b
        incVal s@(Score{sval=sv, multiplier}) = s{sval=sv+multiplier}

enemyColBullet :: Enemy -> Bullet -> World -> World
enemyColBullet e@(Enemy{epos=pos}) b w@(World{bullets=bs, enemies=es, score=s})
    | doBoxesCollide elp erp blp brp = w{bullets=delete b bs, enemies=delete e es, score=incVal s}
    | otherwise = w
    where
        blp = bpos' - bSizeH -- bullet corners
        brp = bpos' + bSizeH
        elp = pos - eSizeH -- enemy corners
        erp = pos + eSizeH
        bpos' = bpos b
        incVal s@(Score{sval=sv, multiplier}) = s{sval=sv+multiplier}

因为这对我来说看起来效率很低,所以我认为应该有一种方法只需要编写where子句,并以某种方式使它包含在两个函数中?

如果有人能帮我解决这个问题,我们将不胜感激!

祝你好运, Skyfe。

2 个答案:

答案 0 :(得分:2)

  

有没有办法可以使用相同的where子句

为2个不同的函数构建一个结构

where为您提供了与本地范围的绑定。因此,要在两个函数之间进行分享,它们必须是"内部" where范围。

更容易将where子句浮出。例如。计算一次并将xy传递给您的函数。

main = do
   let x = func a - c
       y = func' b - c

   someFunc x y
   someOtherFunc x y

答案 1 :(得分:2)

是的!你是对的,这是可以考虑的因素。 Haskell中一个可以帮助你的很酷的功能是歧义。 Haskell有两种歧义,在这种情况下你可以使用任何一种。第一种歧义称为参数类型,例如,无论[x]是什么,所有x都存在列表类型x,因此实际上存在很多不同的函数reverse :: [x] -> [x],每个类型都可以放在这些列表中。唯一的问题是,有时你需要对这些参数进行约束,这样你就可以它们的东西(reverse受到总模糊度的限制;它不能对元素做任何事情但只能重新排序它们并可能丢弃或重复它们。 约束歧义是一种称为类型的功能。以下是我如何使用它们来适应您的情况:

type Size = ____ -- fill this in with whatever that type actually is.

class Sprite s where
    kill :: s -> World -> World
    position :: s -> Size
    size :: s -> Size

instance Sprite Bullet where
    kill b w = w{bullets = delete b (bullets w)}
    position = bpos
    size = const bSizeH -- constant for all bullets, right?

instance Sprite Bonus where
    kill bn w = w{bonuses = delete bn (bonuses w)}
    position = bnpos
    size = const bnSizeH

instance Sprite Enemy where
    kill e w = w{enemies = delete e (enemies w)}
    position = epos
    size = const eSizeH

现在你可以写一些更通用的东西:

 collides :: (Sprite x, Sprite y) => x -> y -> Bool
 collides x y = doBoxesCollide (px - sx) (px + sx) (py - sy) (py + sy)
     where px = position x
           py = position y
           sx = size x
           sy = size y

addScore :: World -> World
addScore w = w{score = s{sval = sval s + multiplier s}} where s = score w

killCollision :: (Sprite x, Sprite y) => x -> y -> World -> World
killCollision x y = if collides x y then addScore . kill x . kill y else id

我们已经从你的22行上升到27行,但我们现在有很多较小的部分以更清晰的方式进行交互,而不是重复自己。无论这是否值得,这都是一种艺术选择 - 它通常有助于程序的长期维护,但如果你只想尝试一个程序,那么复制粘贴通常会更快。

所以,现在你可以写bonusColBullet bn b w,你可以写killCollision bn b w并且它也会做同样的事情,killCollision e b w也适用于敌人e。但是你有更多的力量。假设你想让敌人吃掉他们碰撞的奖金:但是你没有得到这个分数。然后那将是if collides e bn then kill bn else id。或者你可能会认为不同的精灵有不同的points值;然后你添加到Spritepoints :: x -> Points(其中Points是你的积分的任何Num类型 - 大概是Integer,但我不想假设)。您将addScore修改为:

addScore :: (Sprite x, Sprite y) => x -> y -> World -> World
addScore x y w = w{score = s{sval = newscore}
    where s = score w
          newscore = sval s + multiplier s * (points x + points y)

并通过将killCollision替换为addScore来修改addScore x y。而已。现在不同的敌人或奖金可能值不同。通过更多的工作,你可以让一个吃掉奖金的敌人从该奖励中获得积分(这样,如果你杀死它们,你仍然可以得到奖励积分)。这样的东西。