Создание функций над перечислениями - PullRequest
13 голосов
/ 06 июня 2011

Я только начал изучать Haskell.Я думаю, что у меня есть основы, но я хочу убедиться, что я на самом деле заставляю себя думать также и функционально.создайте функцию для отображения между каждым "Dir" и его противоположностью / inv.Я знаю, что мог бы легко продолжить это еще на 3 строки, но я не могу не задаться вопросом, есть ли лучший способ.Я попытался добавить:

inv a = b where inv b = a

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

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

Ответы [ 5 ]

18 голосов
/ 06 июня 2011

Если сопряжение между Up и Down и т. Д. Является важной особенностью, то, возможно, это знание должно быть отражено в типе.

data Axis = UpDown | LeftRight | FrontBack
data Sign = Positive | Negative
data Dir = Dir Axis Sign

inv теперь просто.

2 голосов
/ 06 июня 2011
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]]
2 голосов
/ 06 июня 2011

Есть ли у вас решение в замкнутой форме по индексам, которое соответствует этой функции? Если да, то вы можете использовать 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, использует упорядочение конструкторов и не является Хаскелли. Компромисс состоит в том, чтобы использовать больше типов , чтобы определить отображение между конструкторами и их зеркалами, избегая использования арифметики.

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

Так что, по крайней мере, нам не нужно было заниматься арифметикой. И типы различают зеркальные шкафы. Тем не менее, это очень не Хаскели. Абсолютно хорошо перечислять дела! Другие должны будут прочитать ваш код в какой-то момент ...

1 голос
/ 07 июня 2011

Хорошо знать, что 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
1 голос
/ 07 июня 2011

Я не думаю, что я бы порекомендовал это, но простой ответ в моем уме был бы добавить это:

inv x = fromJust $ find ((==x) . inv) [Right, Front, Up]

Я не мог удержаться от подстройки ответа Ландеи, чтобы соответствовать моемустиль;Вот аналогичное и немного более рекомендуемое решение, которое не нуждается в других определениях:

inv a = fromJust $ do pair <- find (a `elem`) invList
                      find (/= a) pair
  where invList = [[Right, Left], [Up, Down], [Front, Back]]

В нем используется монада Maybe.

...