开发者

Creating functions over Enumerations

I just started learning Haskell. I think I've got the basics down, but I want to make sure I'm actually forcing myself to think functionally too.

data Dir = Right | Left | Front | Back | Up | Down deriving (Show, Eq, Enum)
inv Right = Left
inv Front = Back
inv Up = Down

Anyway, the jist of what I'm trying to do is to create a function to map between each "Dir" and its opposite/inv. I know I could easily continue this for another 3 lines, but I can't help but wonder if there's a better way. I tried adding:

inv a = b where inv b = a

but apparently you can't do that. So my开发者_高级运维 question is: Is there either a way to generate the rest of the inverses or an altogether better way to create this function?

Thanks much.


If the pairing between Up and Down and so on is an important feature then maybe this knowledge should be reflected in the type.

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

inv is now easy.


Do you have a closed-form solution over the indices that corresponds to this function? If so, yes, you can use the Enum deriving to simplify things. For example,

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)

Note, this relies on the ordering of the constructors!

*Main> inv Left
Right
*Main> inv Right
Left
*Main> inv Back
Front
*Main> inv Up
Down

This is very C-like, exploits the ordering of constructors, and is un-Haskelly. A compromise is to use more types, to define a mapping between the constructors and their mirrors, avoiding the use of arithmetic.

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))

E.g.

*Main> inv (A Right)
B Left
*Main> inv (B Down)
A Up

So at least we didn't have to do arithmetic. And the types distinguish the mirror cases. However, this is very un-Haskelly. It is absolute fine to enumerate the cases! Others will have to read your code at some point...


pairs = ps ++ map swap ps where
   ps = [(Right, Left), (Front, Back), (Up, Down)]
   swap (a, b) = (b, a)

inv a = fromJust $ lookup a pairs    

[Edit]

Or how about this?

inv a = head $ delete a $ head $ dropWhile (a `notElem`)
        [[Right,Left],[Front,Back],[Up,Down]]


It is good to know, that Enumeration starts with zero.

Mnemonic: 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


I don't think I'd recommend this, but the simple answer in my mind would be to add this:

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

I couldn't resist tweaking Landei's answer to fit my style; here's a similar and slightly-more-recommended solution that doesn't need the other definitions:

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

It uses the Maybe monad.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜