Подтвердить что ты не робот

Создание функций над перечислениями

Я только начал изучать 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

но, видимо, вы не можете этого сделать. Поэтому мой вопрос: есть ли способ генерировать остальные инверсии или вообще лучший способ создать эту функцию?

Большое спасибо.

4b9b3361

Ответ 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