2014-02-05 3 views
3

У меня есть следующий код в 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 

Вы заметите, что moveForward и moveBackward функции имеют одинаковые where заявления. Есть ли способ удалить это дублирование? У меня есть множество функций с теми же предложениями where (читай: более двух).

Я бы предпочел не передавать его в качестве другого аргумента - так как он никогда не изменится. Он всегда будет cRotation.

+1

Рассматривали ли вы переход от неловкого '(Double, Double, Double)' к чему-то выделенному, например ['Data.Vect'] (http://hackage.haskell.org/package/vect-0.4.7/ Docs/Data-Vect-Double-base.html # т: vec3)? – leftaroundabout

+1

@leftaroundabout У меня есть - я жду от двух вещей, однако. Во-первых, потому что эта тема для меня нова, все меняется ежедневно. Во-вторых, я не уверен, где мне придется оптимизировать определенные вещи. После того, как все станет более конкретным, я взгляну на «Data.Vect», спасибо! – sdasdadas

ответ

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) 

Редактировать: Обзор моего ответа через шесть месяцев, я отмечаю, есть еще некоторое дублирование, которые могут быть сняты OUT: 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) 

Это дает меньше энергии для moveForward и moveBackward, конечно, так как вы не можете использовать их для ничего не делайте. Но это прекрасно отвлекает их от своих сущностей и гарантирует, что вы не можете случайно сделать что-то другое, кроме как двигаться.

+0

Думаю, я буду использовать это. Спасибо! – sdasdadas

2

Там в простой ответ просто определить свои собственные функции

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

И тогда вы могли бы использовать лямбда

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. Что касается удаления, что лямбда, существует не так много вы можете сделать, кроме определения новой функции

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-х кортежей.

+0

По-моему, это менее читаемо на первый взгляд. (Возможно, это связано с тем, что я все еще не продвинулся в Haskell.) EDIT: Хотя я просил удалить дублирование, я взвешиваю его против удобочитаемости. – sdasdadas

+1

@sdasdadas Я бы не сказал, что 'apply3' особенно читаем. Решение amalloy довольно чистое, я думаю, что это, наверное, лучше. – bheklilr

Смежные вопросы