2012-12-12 26 views
5

Sto lavorando ad un tutorial F # che crea un mazzo di carte. I tipi sono elencati, ma non riesco a capire come scorrere i tipi per creare la mappa del full deck. Mi aspettavo di fare qualcosa comeF # Types and Looping

Foreach rank in ranks 
    Foreach suit in suits 
     somehow combine the two 
    next suit 
next rank 

Non c'è modo di fare questo? Di seguito sono riportati i tipi creati.

Penso che se li avessi modificati da tipi a elenchi, avrebbero potuto unire, giusto? Quindi, qual è il punto di tipi?

type suits= 
    |Spade=1 
    |Heart=2 
    |Club=3 
    |Diamond=4 

type ranks= 
    |ValCard of int 
    |Jack 
    |Queen 
    |King 

type deck= Deck of ranks * suits 

risposta

4

Enums è una buona scelta per rappresentare le carte. Hai un confronto tra i semi e tra i ranghi gratis e converti facilmente enumerazioni da/a int.

type suit = 
    | Spade = 1 
    | Heart = 2 
    | Club = 3 
    | Diamond = 4 

type rank = 
    | Ace = 1 | Two = 2 | Three = 3 | Four = 4 | Five = 5 | Six = 6 | Seven = 7 
    | Eight = 8 | Nine = 9 | Ten = 10 | Jack = 11 | Queen = 12 | King = 13 

/// 'Card' is a type which represents a particular card  
type Card = Card of rank * suit 

/// 'deck' is a list consisting of all cards in a full deck 
let deck = [ for r in 1..13 do 
       for s in 1..4 do 
       yield Card(enum<rank> r, enum<suit> s) ] 

Se si va per discriminated unions, è necessario effettuare manualmente gli elenchi di tutti i suit s e tutti rank s. Il vantaggio è un migliore abbinamento di modelli di DU rispetto a quello delle enumerazioni.

type suit = 
    | Spade 
    | Heart 
    | Club 
    | Diamond 

type rank = | Ace | Two | Three | Four | Five | Six | Seven 
      | Eight | Nine | Ten | Jack | Queen | King 

type Card = Card of rank * suit 

let private suits = [Spade; Heart; Club; Diamond] 
let private ranks = [Ace; Two; Three; Four; Five; Six; Seven; 
        Eight; Nine; Ten; Jack; Queen; King] 

let deck = [ for rank in ranks do 
       for suit in suits do 
       yield Card(rank, suit) ] 
+2

DUs forniscono inoltre il confronto "gratuitamente" (sulla base di ordine caso). – Daniel

+0

Non penso che l'enum sia una buona scelta. Puoi anche cambiare i valori del ciclo in "-10..20" e "0..6" e il tuo codice crea ancora alcune "carte" non valide. E come notato, hai anche una comparazione su DU come gratuito. –

5

Un approccio alternativo che utilizza un'unione discriminata, che ingrana più ben oltre le enumerazioni con la sintassi 's F #

type suit= 
    |Spade 
    |Heart 
    |Club 
    |Diamond 
    static member all = [Spade;Heart;Club;Diamond] 

type rank= 
    |ValCard of int 
    |Jack 
    |Queen 
    |King 
    static member all =([1..10]|> List.map (ValCard)) @ [Jack;Queen;King] 

type card = |Card of rank * suit 

let all_cards = suit.All |> List.collect (fun s -> rank.all |> List.map (fun r -> Card(r,s)) 

allora si può fare qualche modello pulito corrispondenza come

all_cards 
|> List.iter (fun c -> 
    match c with 
    |Card(King,Spade) -> ... 
    |Card(King,_) -> ... 
    |Card(_) -> ... 

si potrebbe anche definire alcuni modelli attivi per ottenere carte rosse/nere.

+1

Hai dimenticato 'Valcard (10)'; è lo svantaggio di elencare manualmente tutti i valori. – pad

+0

@pad - buona individuazione - commutato sulla mappa in un elenco –

+1

Si noti che 'ValCard di int' è un'astrazione che perde dal momento che' ValCard 0' e 'ValCard 11', ecc. Sono valori non validi. – pad

2

Come un addendum alla risposta di pad, si potrebbe anche utilizzare la riflessione per generare il ponte:

type Union<'T> private() = 
    static member val Cases = 
    FSharpType.GetUnionCases(typeof<'T>) 
    |> Array.map (fun case -> FSharpValue.MakeUnion(case, null) :?> 'T) 

let deck = 
    [ for rank in Union<rank>.Cases do 
     for suit in Union<suit>.Cases do 
     yield Card(rank, suit) ]