2012-08-28 15 views
5

Esta es mi solución a ejercer de yaht:¿Puede esta muestra de Haskell ser más corta?

Ejercicio 4.6 Escribir una tupla tipo de datos que puede contener uno, dos, tres o cuatro elementos, dependiendo del constructor (es decir, no debe haber cuatro constructores , uno para cada número de argumentos). También proporcione funciones tuple1 a tuple4 que toman una tupla y devuelven el valor Just en esa posición, o Nothing si el número es válido (es decir, pregunta por la tupla4 en una tupla que contiene solo dos elementos).

cuando escribí una primera línea Yo estaba emocionado por la simplicidad en comparación con C#

 

    data Tuplex a b c d = Tuple1 a | Tuple2 a b | Tuple3 a b c | Tuple4 a b c d 

    -- class Tuplex<a,b,c,d> { 
    --  Tuplex(a p1){ _p1 = p1; } 
    --  Tuplex(a p1, b p2){ _p1 = p1; _p2 = p2; } 
    --  Tuplex(a p1, b p2, c p3){ _p1 = p1; _p2 = p2; _p3 = p3; } 
    --  Tuplex(a p1, b p2, c p3, d p4){ _p1 = p1; _p2 = p2; _p3 = p3; _p4 = p4; } 
    --  public Nullable<a> _p1; 
    --  public Nullable<b> _p2; 
    --  public Nullable<c> _p3; 
    --  public Nullable<d> _p4; 
    -- } 

En C# Puedo tener acceso a cualquier campo sin ningún problema, pero aquí yo debería escribir un 'funciones de acceso', ¿verdad? Y la cantidad de código aquí me pone triste.

¿Puedo tener un código más corto aquí?

 

    tuple1 ∷ Tuplex a b c d → Maybe a 
    tuple2 ∷ Tuplex a b c d → Maybe b 
    tuple3 ∷ Tuplex a b c d → Maybe c 
    tuple4 ∷ Tuplex a b c d → Maybe d 
    tuple1 (Tuple1 a) = Just a 
    tuple1 (Tuple2 a b) = Just a 
    tuple1 (Tuple3 a b c) = Just a 
    tuple1 (Tuple4 a b c d) = Just a 
    tuple2 (Tuple1 a) = Nothing 
    tuple2 (Tuple2 a b) = Just b 
    tuple2 (Tuple3 a b c) = Just b 
    tuple2 (Tuple4 a b c d) = Just b 
    tuple3 (Tuple1 a) = Nothing 
    tuple3 (Tuple2 a b) = Nothing 
    tuple3 (Tuple3 a b c) = Just c 
    tuple3 (Tuple4 a b c d) = Just c 
    tuple4 (Tuple1 a) = Nothing 
    tuple4 (Tuple2 a b) = Nothing 
    tuple4 (Tuple3 a b c) = Nothing 
    tuple4 (Tuple4 a b c d) = Just d 

    -- unit tests 
    prop_tx1 = tuple1 (Tuple1 4) ≡ Just 4 
    prop_tx2 = tuple1 (Tuple2 4 'q') ≡ Just 4 
    prop_tx3 = tuple2 (Tuple1 4) ≡ (Nothing ∷ Maybe Char) 
    prop_tx4 = tuple2 (Tuple2 4 'q') ≡ Just 'q' 

+0

Por cierto una de las ventajas # C aquí es lo mejor en comparación con anulable . En C# tenemos sintaxis especial (?postfix) y la conversión completamente transparente (pero aún opcionalmente controlable) entre T? y T (int? y int). Incluso durante el desempaquetado, puedo tener el objeto x = 5; var y = (int?) x. Sin mencionar la conversión innecesaria de int b = 5; a int? c = b; –

+0

Oh, tantas respuestas y formas ... ¿Y sabes qué? Nada es lo suficientemente legible si tenemos en cuenta una cantidad de conocimiento que el autor da en YAHT en ese punto de ejercicio, excepto la respuesta del 1º Daniel ... Claro, leer más puede arrojar algo de luz sobre 'dónde' o '>> =' o '> =>' operadores ... Pero incluso esta respuesta es bastante compleja, creo, no lo adivinaría por mi cuenta, parece complicado :) Muchas gracias por las respuestas. –

Respuesta

7

Aquí hay una manera: centralice su coincidencia de patrón.

unTuplex f1 f2 f3 f4 t = case t of 
    Tuple1 a  -> f1 a 
    Tuple2 a b  -> f2 a b 
    Tuple3 a b c -> f3 a b c 
    Tuple4 a b c d -> f4 a b c d 

tuple1 = unTuplex (\a -> Just a) (\a _ -> Just a) (\a _ _ -> Just a) (\a _ _ _ -> Just a) 
tuple2 = unTuplex (\_ -> Nothing) (\_ b -> Just b) (\_ b _ -> Just b) (\_ b _ _ -> Just b) 
tuple3 = unTuplex (\_ -> Nothing) (\_ _ -> Nothing) (\_ _ c -> Just c) (\_ _ c _ -> Just c) 
tuple4 = unTuplex (\_ -> Nothing) (\_ _ -> Nothing) (\_ _ _ -> Nothing) (\_ _ _ d -> Just d) 

Alternativamente, se puede expresar de manera explícita la estructura anidada:

{-# LANGUAGE NoMonomorphismRestriction #-} 
data DONE = DONE -- could just use(), but this is a pretty descriptive name 
type Tuplex a b c d = Maybe (a, Maybe (b, Maybe (c, Maybe (d, DONE)))) 

tuple1 x = x >>= return . fst -- or tuple1 = fmap fst 
tuple2 x = x >>= tuple1 . snd 
tuple3 x = x >>= tuple2 . snd 
tuple4 x = x >>= tuple3 . snd 

Entonces tuple1 tiene (entre otros) el tipo Tuplex a b c d -> Maybe a, y en un máximo de tuple4 que tiene (de nuevo, entre otros) el tipo Tuplex a b c d -> Maybe d.

editar: ... en realidad, esto sugiere una continuación alternativa del primer enfoque.

import Control.Monad 

decrement :: Tuplex a b c d -> Maybe (Tuplex b c d t) 
decrement (Tuple1 a) = Nothing 
decrement (Tuple2 a b) = Just (Tuple1 b) 
decrement (Tuple3 a b c) = Just (Tuple2 b c) 
decrement (Tuple4 a b c d) = Just (Tuple3 b c d) 

zero :: Tuplex a b c d -> a 
zero (Tuple1 a) = a 
zero (Tuple2 a b) = a 
zero (Tuple3 a b c) = a 
zero (Tuple4 a b c d) = a 

tuple1 = Just . zero 
tuple2 = decrement >=> tuple1 
tuple3 = decrement >=> tuple2 
tuple4 = decrement >=> tuple3 
1

¡Solo dé sus nombres de campo de tuplas!

data Tuplex a b c d = Tuple1 { tuple1 :: a } 
        | Tuple2 { tuple1 :: a 
          , tuple2 :: b } 
        | Tuple3 { tuple1 :: a 
          , tuple2 :: b 
          , tuple3 :: c } 
        | Tuple4 { tuple1 :: a 
          , tuple2 :: b 
          , tuple3 :: c 
          , tuple4 :: d } 

Y como resultado que tiene funciones con tipos de:

tuple1 :: Tuplex a b c d -> a 
tuple2 :: Tuplex a b c d -> b 
-- etc 

Uso de nombres de los campos de los registros de este tipo es en realidad menos común de lo que se podría esperar en Haskell, debido a la facilidad de coincidencia de patrones y , al menos en algunos círculos, la popularidad de la extensión RecordWildCard que le permite hacer cosas como:

function (Tuples3 {..}) = 
-- now you have variables tuple1 :: a, tuple2 :: b, etc. 

(al utilizar comodines de registro que podría ser Bett er para nombrar los campos de tuplas algo un poco más simple, como tupA, tupB, tupC, tupD)

+7

El otro problema, al menos en este ejemplo, es que las funciones de acceso son parciales. Algo como 'tuple2 (Tuple1" foo ")' se rompería. En este caso particular, y en la mayoría de los códigos normales, imagino, desea una manera más elegante de manejar accesos inválidos como ese (por ejemplo, devolver un 'Maybe' como se especifica en la pregunta). –

+0

Ah, no me di cuenta de que usó 'Maybe'. La herramienta 'deriva' probablemente puede hacer eso y si no, entonces debería ser parcheado. Además, hay poca diferencia con su uso y un 'datos Tup abcd = Tup {tA :: (Tal vez a), tB :: (Tal vez b) ...}' (más algunas funciones auxiliares/constructores) así que tal vez eso sería una mejor solución. –

1
import Safe (atMay) -- from the 'safe' package 

toList (Tuple1 a) = [a] 
toList (Tuple2 a b) = [a, b] 
toList (Tuple3 a b c) = [a, b, c] 
toList (Tuple4 a b c d) = [a, b, c, d] 

tuple n t = atMay (toList t) n 

[tuple1, tuple2, tuple3, tuple4] = map tuple [1..4] 

Editar: Vito correctamente señalan que esto sólo funciona para una tupla homogénea, por lo que esto no es una respuesta correcta. En ese caso, difiero a la respuesta de Daniel.

+1

Si no desea utilizar un paquete externo por alguna razón, también puede hacer algo como 'listToMaybe. soltar n'. –

+1

Esto funciona solo si todos los elementos de tupla tienen el mismo tipo. – Landei

+1

Tenga en cuenta que 'tuple1 :: TupleX a a a a -> Tal vez a' y no el más genérico' TupleX a b c d -> Maybe a'. – Vitus

7

me gustaría tratar de mantenerlo simple muerto:

data Tuplex a b c d = Tuple1 a | Tuple2 a b | Tuple3 a b c | Tuple4 a b c d 

toMaybes (Tuple1 p)  = (Just p, Nothing, Nothing, Nothing) 
toMaybes (Tuple2 p q)  = (Just p, Just q, Nothing, Nothing) 
toMaybes (Tuple3 p q r) = (Just p, Just q, Just r, Nothing) 
toMaybes (Tuple4 p q r s) = (Just p, Just q, Just r, Just s) 

tuple1 t = p where (p,_,_,_) = toMaybes t 
tuple2 t = q where (_,q,_,_) = toMaybes t 
tuple3 t = r where (_,_,r,_) = toMaybes t 
tuple4 t = s where (_,_,_,s) = toMaybes t 
+0

Parece más simple y comprensible pero en el momento de leer este libro no estoy familiarizado con el operador 'where' –

+0

Esto es lo mismo que 'tuple1 t = let (p, _, _, _) = toMaybes t in p'. En la mayoría de los casos, puede ver 'where' como una especie de 'reverse' let'". Sin embargo, existen algunas diferencias con respecto a los ámbitos, etc. – Landei

Cuestiones relacionadas