2012-08-28 33 views
5

Questa è la mia soluzione di esercitare da yaht:Questo esempio di haskell può essere più breve?

Esercizio 4.6 Scrivi una tupla tipo di dati che può contenere uno, due, tre o quattro elementi, a seconda del costruttore (vale a dire, non ci dovrebbe essere quattro costruttori , uno per ogni numero di argomenti). Fornisci anche le funzioni tuple1 a tuple4 che prendono una tupla e restituiscono solo il valore in quella posizione, oppure Nothing se il numero è valido (ad esempio, chiedi la tuple4 su una tupla che contiene solo due elementi).

quando ho scritto una prima linea ero entusiasta di semplicità confronto 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; 
    -- } 

In C# posso accedere a qualsiasi campo senza problemi, ma qui avrei dovuto scrivere un 'funzioni di accesso', giusto? E la quantità di codice qui mi rende triste.

Posso avere un codice più breve qui?

 

    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

BTW uno dei C# vantaggi Ecco Forse rispetto a Nullable . In C# abbiamo una sintassi speciale (?postfix) e conversione completamente trasparente (ma ancora opzionalmente controllabile) tra T? e T (int? e int). Anche durante l'unboxing, posso avere oggetto x = 5; var y = (int?) x. Non menzionare la conversione inutile da int b = 5; a int? c = b; –

+0

Oh, tante risposte e modi ... E sai cosa? Nulla è leggibile abbastanza se prendiamo in considerazione una quantità di conoscenza che l'autore dà in YAHT in quel punto di esercizio, ad eccezione della prima risposta di Daniel ... Certo, un'ulteriore lettura può far luce su "dove" o ">> =" o '> =>' operatori ... Ma anche questa risposta è piuttosto complessa, penso, non indovinerei che, da parte mia, sembra difficile :) Grazie mille per le risposte. –

risposta

7

Ecco un modo: centralizzare la corrispondenza del modello.

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) 

In alternativa, si potrebbe esprimere in modo esplicito la struttura annidata:

{-# 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 

Poi tuple1 ha (tra gli altri) il tipo Tuplex a b c d -> Maybe a, e su un massimo di tuple4 che ha (ancora una volta, tra gli altri) il tipo Tuplex a b c d -> Maybe d.

modifica: ... in realtà, questo suggerisce una continuazione alternativa del primo approccio.

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

Basta dare i nomi dei campi delle tuple!

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 } 

E come risultato si hanno le funzioni con i tipi di:

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

utilizzando i nomi dei campi di dischi come questo è in realtà meno comune di quanto ci si potrebbe aspettare in Haskell a causa della facilità di pattern matching e , almeno in alcuni ambienti, la popolarità del prolungamento RecordWildCard, che ti permette di fare cose come:

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

(quando si utilizzano le wild card di registrazione potrebbe essere Bett per nominare i campi della tupla qualcosa di più semplice, come tupA, tupB, tupC, tupD)

+7

L'altro problema, almeno in questo esempio, è che le funzioni di accesso sono parziali. Qualcosa come 'tuple2 (Tuple1" foo ")' si spezzerebbe. In questo caso particolare - e nella maggior parte del codice normale, immagino - vuoi un modo più elegante per gestire accessi non validi come quello (ad esempio restituendo un 'Maybe' come specificato nella domanda). –

+0

Ah, non ho notato che usava 'Maybe'. Lo strumento 'derive' può probabilmente farlo e se non è così dovrebbe essere corretto. Inoltre, c'è una piccola differenza nel suo uso e un 'data Tup abcd = Tup {tA :: (Forse a), tB :: (Forse b) ...}' (più alcune funzioni di aiuto/costruttori) quindi forse sarebbe una soluzione migliore. –

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] 

Edit: Vitus giustamente sottolineare che questo funziona solo per una tupla omogenea, quindi questo non è una risposta corretta. In tal caso rimando alla risposta di Daniel.

+1

Se non si desidera utilizzare un pacchetto esterno per qualsiasi motivo, si potrebbe anche fare qualcosa come 'listToMaybe. goccia n'. –

+1

Funziona solo se tutti gli elementi di tupla hanno lo stesso tipo. – Landei

+1

Ricorda che 'tuple1 :: TupleX a a a a -> Forse a' e non il più generico' TupleX a b c d -> Forse a'. – Vitus

7

cercherei di tenerlo morto semplice:

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

Sembra più semplice e comprensibile ma al punto di leggere questo libro non ho familiarità con l'operatore 'where' –

+0

Questo è lo stesso di 'tuple1 t = let (p, _, _, _) = toMaybes t in p'. Nella maggior parte dei casi puoi vedere 'where' come una sorta di" inverso 'let'". Ci sono alcune differenze riguardo gli ambiti ecc., Comunque. – Landei

Problemi correlati