2013-01-04 18 views
5

Sto imparando Haskell, quindi sto scrivendo alcuni semplici giochi di carte. Ho definito alcuni tipi di dati:Haskell: Come generare un prodotto cartesiano di due semplici tipi di dati algebrici

data Rank = Ace|Two|Three|Four|Five|Six|Seven|Eight|Nine|Ten|Jack|Queen|King deriving (Eq,Show,Ord) 

data Suit = Hearts|Spades|Diamonds|Clubs deriving (Show) 

data Card = Card Rank Suit 

Ora mi piacerebbe creare un mazzo di 52 carte. Sono sicuro che c'è un modo per farlo chiazza di petrolio, ma tutto quello che posso venire in mente è:

pristineDeck = [Card Ace Hearts, Card Two Hearts, ...] 

Posso ottenere Haskell per generare questa lista per me?

+2

Fai in modo che i tipi generino la classe di caratteri "Enum" (può accadere semplicemente mettendo Enum accanto a 'Mostra' lassù). I tre di loro: Rank, Suit e Card. –

+1

P.S. quello che stai cercando non è il prodotto incrociato, che è qualcosa che coinvolge i vettori 3D. Probabilmente intendevi "prodotto cartesiano". –

+0

@BenMillwood Il mio male ... "SQL cross join" + "prodotto cartesiano" + laurea in fisica –

risposta

10

La comprensione delle liste è una sintassi molto ordinata per questo. Se si deriva Enum su Rank e Suit si può esprimere in modo molto semplice come:

pristineDeck = [ Card rank suit | suit <- [Hearts .. Clubs], rank <- [Ace .. King] ] 

Se vi state chiedendo il motivo per cui ho suit e rank in ordine diverso, il primo è a causa dell'ordine del costruttore Card usa, mentre il secondo è quello di ottenere l'ordine della lista risultante - si adatta insieme in ordine crescente.

In termini più generici, o quando una singola list comprehension diventa troppo voluminosa, il prodotto cartesiano è esattamente il comportamento fornito dall'istanza Monad per gli elenchi. Quello che segue è equivalente alla lista di comprensione di cui sopra:

pristineDeck = do suit <- [Hearts .. Clubs] 
        rank <- [Ace .. King] 
        return $ Card rank suit 

Come un altro piccolo punto, per risparmiare la fatica di ricordare quale ordine i valori Suit sono, derivante Bounded così permetterà di scrivere [minBound .. maxBound] per enumerare tutti valori di qualsiasi tipo con istanze di entrambi Enum e Bounded.

+1

Personalmente non deriverei 'Enum' per il tipo' Suit', e scrivo solo la lista completa nella comprensione. I semi non hanno un ordinamento naturale, IMO, quindi la sintassi dei puntini di sospensione è confusa. –

+2

E vorrei anche ricavare un'istanza "Bounded". '[minBound .. maxBound]' rende chiaro che stai enumerando tutte le varianti, anche se non esiste un ordinamento naturale. –

+0

@ C.A. McCann Stai attento a digitare '[Hearts .. Clubs]' e '[Ace .. King]', altrimenti riceverai un errore: 'Not in scope: 'Hearts ..''. –

7

Ci sono diversi modi per farlo, di quantità variabili di magia.

Innanzitutto, poiché nessuno dei costruttori del tuo tipo ha argomenti, è possibile derivare da essi Enum. Ciò ti consentirà di scrivere ad es. [Ace..King] per ottenere un elenco di tutte le carte.

In secondo luogo, le list comprehensions sono un ottimo modo per creare un elenco di elementi tratti da più elenchi. Prova questo:

[x + y | x <- [100,200,300], y <- [1,2,3]] 

Questo dovrebbe darti gli strumenti necessari per applicare al tuo esempio.

3

Alp è corretta sul dirvi derivare Enum

>data Rank = Ace|Two|Three|Four|Five|Six|Seven|Eight|Nine|Ten|Jack|Queen|King deriving (Eq,Show,Ord,Enum) 
>data Suit = Hearts|Spades|Diamonds|Clubs deriving (Show,Enum) 

Ora:

>enumFrom Ace 
[Ace,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Jack,Queen,King] 

Per ottenere le permutazioni di due liste è possibile utilizzare una lista di comprensione:

>[[x,y]|x<-[1..2],y<-[2..5]] 
[[1,2],[1,3],[1,4],[1,5],[2,2],[2,3],[2,4],[2,5]] 

o per ottenere le permutazioni di aggiunta:

>[x + y|x<-[1..2],y<-[2..5]] 
[3,4,5,6,4,5,6,7] 

Ora hai solo bisogno di fare alcune sostituzioni per ottenere le permutazioni di Car con Rank e Suit.

Problemi correlati