Search code examples
f#functional-programmingimmutabilitypoker

How to generate data idiomatically in f# inline in code


Lets say i am attempting to implement some sort of poker program in f#. Firstly is this the correct use of the type system, massive newbie here.

type Suit =
    | Hearts
    | Diamonds
    | Spades
    | Clubs

type Card = {
    Suit:Suit
    Rank:int
}

type Hand = {
    Cards:List<Card>
}

Anyway suppose i want a function to return me a randomised list of the possible holding cards. I guess there would be two functions which i would chain together, however i am having a hard time implementing them without creating a loads of objects. each of the functions in the list module will return a new list and the let keyword makes impossible to change value of the reference. what is the functional way of achieving this then. So far i have this

let generateCards = {
    let ranks = [ 1..52 ]...

} 

let shuffle cards = {

}

let cards = shuffle generateCards

Solution

  • One way to shuffle a deck would be to zip it with a sequence of random numbers, then sort by those numbers, then unzip again.

    let allCards = 
      [ for rank in 2..14 do
          for suit in [Hearts; Diamonds; Spades; Clubs] do
            yield { Suit = suit; Rank = rank } ]
    
    let shuffle cards = 
        let rnd = System.Random()
        let rndSequence = Seq.initInfinite (fun _ -> rnd.Next())
        Seq.zip cards rndSequence |> Seq.sortBy snd |> Seq.map fst |> Seq.toList
    
    shuffle allCards
    

    The above could also be simplified (nod to @DaveShaw's comment), though at a cost of making it less obvious to a human, by sorting the sequence by a randomly generated key:

    let shuffle cards = 
        let rnd = System.Random()
        cards |> List.sortBy (fun _ -> rnd.Next())
    

    Or even simpler (though, possibly, with more performance impact):

    let shuffle cards = List.sortBy (fun _ -> System.Guid.NewGuid()) cards
    

    And for ultimate simplicity, go point-free style:

    let shuffle = List.sortBy (fun _ -> System.Guid.NewGuid())