Создание функций над перечислениями
Я только начал изучать Haskell. Я думаю, что у меня есть основы, но я хочу убедиться, что я на самом деле заставляю себя думать также функционально.
data Dir = Right | Left | Front | Back | Up | Down deriving (Show, Eq, Enum)
inv Right = Left
inv Front = Back
inv Up = Down
В любом случае, сущность того, что я пытаюсь сделать, - создать функцию для отображения между каждым "Dir" и его противоположностью /inv. Я знаю, что я мог бы легко продолжить это для еще 3 строк, но я не могу не задаться вопросом, есть ли лучший способ. Я попытался добавить:
inv a = b where inv b = a
но, видимо, вы не можете этого сделать. Поэтому мой вопрос: есть ли способ генерировать остальные инверсии или вообще лучший способ создать эту функцию?
Большое спасибо.
Ответы
Ответ 1
Если соединение между Up
и Down
и т.д. является важной особенностью, возможно, это знание должно быть отражено в типе.
data Axis = UpDown | LeftRight | FrontBack
data Sign = Positive | Negative
data Dir = Dir Axis Sign
inv
теперь легко.
Ответ 2
У вас есть решение с закрытой формой по индексам, которые соответствуют этой функции? Если да, то вы можете использовать Enum
для упрощения. Например,
import Prelude hiding (Either(..))
data Dir = Right
| Front
| Up
| Left
| Back
| Down
deriving (Show, Eq, Ord, Enum)
inv :: Dir -> Dir
inv x = toEnum ((3 + fromEnum x) `mod` 6)
Обратите внимание, что это зависит от упорядочения конструкторов!
*Main> inv Left
Right
*Main> inv Right
Left
*Main> inv Back
Front
*Main> inv Up
Down
Это очень C-like, использует порядок конструкторов и является не-Haskelly. Компромисс заключается в том, чтобы использовать больше типов, чтобы определить сопоставление между конструкторами и их зеркалами, избегая использования арифметики.
import Prelude hiding (Either(..))
data Dir = A NormalDir
| B MirrorDir
deriving Show
data NormalDir = Right | Front | Up
deriving (Show, Eq, Ord, Enum)
data MirrorDir = Left | Back | Down
deriving (Show, Eq, Ord, Enum)
inv :: Dir -> Dir
inv (A n) = B (toEnum (fromEnum n))
inv (B n) = A (toEnum (fromEnum n))
например.
*Main> inv (A Right)
B Left
*Main> inv (B Down)
A Up
Итак, по крайней мере, нам не нужно было делать арифметику. И типы различают зеркальные шкафы. Однако это очень не-Хаскелли. Абсолютно точно перечислять случаи! Другие должны будут прочитать ваш код в какой-то момент...
Ответ 3
pairs = ps ++ map swap ps where
ps = [(Right, Left), (Front, Back), (Up, Down)]
swap (a, b) = (b, a)
inv a = fromJust $ lookup a pairs
[изменить]
Или как насчет этого?
inv a = head $ delete a $ head $ dropWhile (a `notElem`)
[[Right,Left],[Front,Back],[Up,Down]]
Ответ 4
Я не думаю, что рекомендую это, но простой ответ на мой взгляд заключается в том, чтобы добавить это:
inv x = fromJust $ find ((==x) . inv) [Right, Front, Up]
Я не мог удержаться от настройки ответа Landei, чтобы соответствовать моему стилю; здесь похожее и немного более рекомендуемое решение, которое не нуждается в других определениях:
inv a = fromJust $ do pair <- find (a `elem`) invList
find (/= a) pair
where invList = [[Right, Left], [Up, Down], [Front, Back]]
Он использует монаду Maybe.
Ответ 5
Хорошо знать, что Enum
начинается с нуля.
Мнемоника: fmap fromEnum [False,True] == [0,1]
import Data.Bits(xor)
-- Enum: 0 1 2 3 4 5
data Dir = Right | Left | Front | Back | Up | Down
deriving (Read,Show,Eq,Ord,Enum,Bounded)
inv :: Dir -> Dir
inv = toEnum . xor 1 . fromEnum