2011-06-06 17 views
13

Acabo de empezar a aprender Haskell. Creo que tengo lo básico, pero quiero asegurarme de que realmente me obligo a pensar funcionalmente también.Creando funciones sobre Enumeraciones

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

De todos modos, la jist de lo que estoy tratando de hacer es crear una función para mapear entre cada "Dir" y su opuesto/inv. Sé que podría continuar esto fácilmente por otras 3 líneas, pero no puedo evitar preguntarme si hay una mejor manera. Intenté agregar:

inv a = b where inv b = a 

pero aparentemente no se puede hacer eso. Entonces mi pregunta es: ¿hay alguna forma de generar el resto de las inversas o una forma mejor de crear esta función?

Muchas gracias.

Respuesta

18

Si el emparejamiento entre Up y Down y así sucesivamente es una característica importante, entonces tal vez este conocimiento debería reflejarse en el tipo.

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

inv ahora es fácil.

+0

Eso es mejor. Capture con precisión el concepto central (dimensión y dirección) en tipos. –

+0

tiene sentido. Encontrar el inverso de "inv Positive" sobre "Sign" es mejor que encontrar el inverso de "inv Right" sobre todo "Dir". Entonces la única manera de hacer un 'inverso' limpiamente es hacer que lo que estás invirtiendo sea realmente pequeño ... – rcbuchanan

+1

@ rcb451, te estás perdiendo el punto. El hecho de que 'Sign' tenga menos constructores de datos es incidental (aunque ciertamente beneficioso). La observación clave es que la estrecha relación entre 'Izquierda' y 'Derecha' debe reflejarse en sus tipos, así como la relación distante entre (por ejemplo) 'Izquierda' y 'Superior'. El sistema de tipos está ahí para ayudar con las invariantes: con más tipos puede expresar invariantes más precisos. – Lambdageek

2

¿Tiene una solución cerrada sobre los índices que corresponde a esta función? Si es así, sí, puede usar el Enum derivado para simplificar las cosas. Por ejemplo,

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) 

Nota, este se basa en el orden de los constructores!

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

Esto es muy parecido a C, explota el orden de los constructores, y se anula la Haskelly. Un compromiso es utilizar más tipos, para definir un mapeo entre los constructores y sus réplicas, evitando el uso de la aritmética.

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 

Así que al menos no tuvimos que hacer aritmética. Y los tipos distinguen los casos espejo. Sin embargo, esto es muy poco Haskelly. ¡Está absolutamente bien enumerar los casos! Otros tendrán que leer su código en algún momento ...

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

inv a = fromJust $ lookup a pairs  

[Editar]

O qué tal esto?

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

no creo que me gustaría recomendar esto, pero la respuesta simple en mi mente sería añadir esto:

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

no pude resistir ajustar la respuesta de Landei a adaptarse a mi estilo; he aquí una solución similar y ligeramente más recomendado por la que no necesita de las otras definiciones:

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

Utiliza la mónada Maybe.

+0

¿Hm? ¿Error? Eso no es mejor que declarar 'inv x = inv x'. – comonad

+1

Quiero decir agregar esto después de las definiciones que ya escribió para 'inv Right',' inv Front', 'inv Up'. [Vea usted mismo que funciona] (https://ideone.com/Uc9Px). –

+0

Ah, está bien. Con esas líneas adicionales, funciona. – comonad

1

Es bueno saber que la operación Enum comienza con cero.

mnemotécnico: 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 
Cuestiones relacionadas