如何使用F#处理卡片

Cam*_*ley 4 f# functional-programming

我一直致力于使用F#对流行的纸牌游戏(情书)进行建模,以了解有关函数式编程的更多信息.

module Game = 
open Cards
open Players

type Deck = Card list

let dealACard (deck:Deck) = 
    let randomGenerator = System.Random()
    let index = randomGenerator.Next deck.Length
    let card = deck.Item index
    (card, (deck |> List.filter((<>) card)))

let createPlayer playerNumber deck =
    let card, newDeck = dealACard deck
    ({cards=[card]; playerNumber=playerNumber}, newDeck)
Run Code Online (Sandbox Code Playgroud)

我一直做得很好,直到我知道如何模拟如何绘制卡片.为了测试这个,我想从牌组中抽出所有牌.我的程序看起来像这样:

let deck = createDeck
while not deck.IsEmpty do
    let card, newDeck = dealACard deck
    // print the card
    // how do I update the deck?
Run Code Online (Sandbox Code Playgroud)

任何帮助或反馈都会很棒.

Mar*_*ann 6

F#列表是不可变的,所以如果deck.IsEmpty开始false,它将false永远存在.但是,没有理由让事情变得如此复杂.

假设您有一个已排序的牌组.我们只使用三张牌作为例子,但假设它是一个完整的牌组:

let deck =
    [
        { Suit = Hearts; Face = Queen }
        { Suit = Diamonds; Face = King }
        { Suit = Spades; Face = Ace }
    ]
Run Code Online (Sandbox Code Playgroud)

您可以使用随机数生成器轻松地对卡座进行加扰:

let r = Random ()
let scrambledDeck = deck |> List.sortBy (fun _ -> r.Next ())
Run Code Online (Sandbox Code Playgroud)

第一次创建时scrambledDeck,它可能在FSI中看起来像这样:

> let scrambledDeck = deck |> List.sortBy (fun _ -> r.Next ());;

val scrambledDeck : Card list =
  [{Suit = Spades;
    Face = Ace;}; {Suit = Hearts;
                   Face = Queen;}; {Suit = Diamonds;
                                    Face = King;}]
Run Code Online (Sandbox Code Playgroud)

但如果你再次这样做,它可能看起来像这样:

> let scrambledDeck = deck |> List.sortBy (fun _ -> r.Next ());;

val scrambledDeck : Card list =
  [{Suit = Spades;
    Face = Ace;}; {Suit = Diamonds;
                   Face = King;}; {Suit = Hearts;
                                   Face = Queen;}]
Run Code Online (Sandbox Code Playgroud)

现在你有一个混乱的牌组,你可以简单地开始拉出牌,例如为了打印它们:

scrambledDeck |> List.iter (printfn "%O")
Run Code Online (Sandbox Code Playgroud)

  • 对于练习,这将没事.如果您关心洗牌的质量,请考虑Fisher-Yates shuffle.如果您使用此处的版本http://rosettacode.org/wiki/Knuth_shuffle#F.23并将您的库存套牌定义为数组,您可以执行`let scrambledDeck = deckArray |> FisherYatesShuffle |> Seq.toList` (2认同)

Rei*_*ans 2

您可以使用 a 洗牌List.sortBy,然后在方法中执行头尾模式匹配dealACard,以返回Option顶牌和新牌组的 an 或None如果牌组中没有更多牌。

type DealResult = {
    Card : Card
    Deck : Deck
}

let shuffle deck = 
    let random = new System.Random()
    deck |> List.sortBy (fun x -> random.Next())

let dealACard deck =
    match deck with
    | [] -> None
    | card::restOfDeck -> Some { Card = card; Deck = restOfDeck }
Run Code Online (Sandbox Code Playgroud)

您还可以shuffle通过允许应用随机数生成函数来创建更高阶的函数

let shuffle random deck =
    deck |> List.sortBy (fun x -> random())
Run Code Online (Sandbox Code Playgroud)

用法示例

let deck = [{Rank = 1}; {Rank = 2}] |> shuffle
//val deck : Card list = [{Rank = 2;}; {Rank = 1;}]

let draw1 = deck |> dealACard
//val draw1 : DealResult option = Some {Card = {Rank = 2;}; 
//                                      Deck = [{Rank = 1;}];}

let draw2 = match draw1 with 
            | Some d -> d.Deck |> dealACard
            | None -> None
//val draw2 : DealResult option = Some {Card = {Rank = 1;};
//                                Deck = [];}


let draw3 = match draw2 with 
            | Some d -> d.Deck |> dealACard
            | None -> None
//val draw3 : DealResult option = None
Run Code Online (Sandbox Code Playgroud)

按评论添加

为了以不可变的方式跟踪牌组的当前状态,您可能需要某种接受牌组的递归函数

type DealResult = {
    Card : Card option
    Deck : Deck
}

let dealACard deck =
    match deck with
    | [] -> { Card = None; Deck = deck }
    | card::restOfDeck -> { Card = Some card; Deck = restOfDeck }

let rec dealAllCards deck =
    let result = deck |> dealACard
    match result.Card with 
    | None -> printfn "Cards out"
    | Some c -> 
        printfn "%A" c
        result.Deck |> dealAllCards

let deck = [(Two, Hearts); (Three, Hearts); (Four, Hearts)] |> shuffle

dealAllCards deck
//(Three, Hearts)
//(Four, Hearts)
//(Two, Hearts)
//Cards out
Run Code Online (Sandbox Code Playgroud)