有没有办法在Haskell中删除重复的where语句?

时间:2014-02-05 02:21:23

标签: haskell

我在Haskell中有以下代码:

move :: Camera -> (Double, Double, Double) -> Camera
move camera (xt, yt, zt) = camera { cPosition = (x + xt, y + yt, z + zt) }
    where (x, y, z) = cPosition camera

moveForward :: Camera -> Camera
moveForward camera = move camera (-1 * sin ya, 0, -1 * cos ya)
    where (_, ya, _) = cRotation camera

moveBackward :: Camera -> Camera
moveBackward camera = move camera (sin ya, 0, cos ya)
    where (_, ya, _) = cRotation camera

您会注意到moveForwardmoveBackward函数具有相同的where语句。有没有办法删除这种重复?我有许多具有相同where子句的函数(读取:超过两个)。

我宁愿不把它作为另一个论点传递 - 因为它永远不会改变。它始终是cRotation

2 个答案:

答案 0 :(得分:7)

如何使这些函数将元组作为参数,然后用另一个自动执行提取元组的无聊工作的函数包装它们?

rotated :: ((Double, Double, Double) -> Camera -> a) -> Camera -> a
rotated f camera = f (cPosition camera) camera

moveForward :: Camera -> Camera
moveForward = rotated moveForward'
    where moveForward' (_, ya, _) camera = move camera (-1 * sin ya, 0, -1 * cos ya)

moveBackward :: Camera -> Camera
moveBackward = rotated moveBackward'
    where moveBackward' (_, ya, _) camera = move camera (sin ya, 0, cos ya)

编辑:六个月后查看我的答案,我注意到可以解除更多重复:move camera电话。所以像moveForward这样的函数真的可以采用3元组并返回3元组,如下所示:

moveRotated :: ((Double, Double, Double) -> (Double, Double, Double)) -> Camera -> Camera
moveRotated f camera = move camera . f $ cPosition camera

moveForward :: Camera -> Camera
moveForward = moveRotated forward
    where forward (_, ya, _) = (- sin ya, 0, - cos ya)

moveBackward :: Camera -> Camera
moveBackward = moveRotated backward
    where backward (_, ya, _) = (sin ya, 0, cos ya)

这给moveForwardmoveBackward提供了更少的权力,当然,因为除了移动之外你不能用它们做任何事情。但它很好地将它们提炼到它们的本质,并确保你不会意外地做一些其他事情而不是移动。

答案 1 :(得分:2)

只需定义自己的功能就可以得到简单的答案

snd3 :: (a, b, c) -> b
snd3 (a, b, c) = b

然后你可以使用lambda

moveForward camera = \ya -> (-1 * sin ya, 0, -1 * cos ya) $ snd3 $ cRotation camera

moveBackward camera = \ya -> (sin ya, 0, cos ya) $ snd3 $ cRotation camera

或者,如果您要将lens库添加为依赖项,则可以将snd3 cRotation camera替换为cRotation camera ^. _2或等效view _2 $ cRotation camera。至于删除lambda,除了定义一个新函数

之外你没有什么可做的
apply3 :: (a -> a') -> (b -> b') -> (c -> c') -> (a, b, c) -> (a', b', c')
apply3 f1 f2 f3 (a, b, c) = (f1 a, f2 b, f3 c)

moveForward = apply3 (negate . sin) (const 0) (negate . cos) . snd3 . cRotation

moveBackward = apply3 sin (const 0) cos . snd3 . cRotation

并使用一些eta减少。

不幸的是,使用2元组有很多优雅的技巧,但3元组没有那么多。