Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# Types and Looping

Tags:

types

loops

f#

I am working on an F# tutorial that creates a deck of cards. The types are listed, but I cannot understand how to loop through the types to create the map of the full deck. I expected to do something like

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

Is there no way to do this? Below are the types created.

I think if I changed them from types to lists they could union, right? So, what's the point of types?

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

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

type deck= Deck of ranks * suits
like image 995
Uziel Avatar asked Dec 12 '12 06:12

Uziel


3 Answers

An alternative approach which uses a discriminated union which meshes more nicely than enums with F#'s syntax

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))

Then you can do some neat pattern matching like

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

You could even define some Active patterns to get red/black cards .

like image 56
John Palmer Avatar answered Sep 21 '22 04:09

John Palmer


Enums is a good choice for representing cards. You have comparison among suits and among ranks for free, and easily convert enums from/to 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) ]

If you go for discriminated unions, you have to manually make lists of all suits and all ranks. The advantage is better pattern matching of DUs than that of enums.

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) ]
like image 4
pad Avatar answered Sep 22 '22 04:09

pad


As an addendum to pad's answer, you could also use reflection to generate the deck:

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) ]
like image 3
Daniel Avatar answered Sep 20 '22 04:09

Daniel