如何在F#中建模绘图卡

时间:2016-05-01 01:48:46

标签: f#

我正在设计如何从牌组中绘制扑克牌,我想出了以下解决方案:

type Card = { Value:int }
type Deck = Card list
type Player = { Hand : Card list }

type CardDrawer(deck) =
     let mutable deck = deck
     member this.drawCard () =
          match deck with
          | h::t ->
               deck <- t
               h
     member this.getDeck () = deck

//Example Usage
let createPlayers deck numPlayers =
     let cardDrawer = new CardDrawer(deck)
     let drawCard = cardDrawer.drawCard
     let createPlayer drawCard =
         let hand = [drawCard(); drawCard()]
         {Hand=hand}

     ([1 .. numPlayers] |> List.map(fun _ -> createPlayer drawCard)),cardDrawer.getDeck()

这个解决方案的主要问题是我正在使用CardDrawer的可变字段来获取修改后的套牌。我觉得使用计算表达式可以在这里工作,但我不确定如何实现它。

有任何想法/建议吗?

编辑: 这是一个替代设置,但是我如何让createPlayer知道如何获取变异输出并将其转回输入?

// Create a function that draws a card
let drawCard deck ()= 
     match deck with
     | h::t -> (h,t)

// Function given a deck and number of players, will return all the players and the deck
let createPlayers deck numPlayers =

     // Function given a deck, will return the player and the new deck
    let createPlayer deck =
        let card, deck = drawCard deck ()
        let card', deck = drawCard deck ()
        { Hand = [card; card'] }, deck

     // But what would this look like now? 
     // How can I get the deck coming back from createPlayer 
     // used for the input to the next time createPlayer is called?
     ([1 .. numPlayers] |> List.map(fun _ -> createPlayer deck)

1 个答案:

答案 0 :(得分:5)

您可以将其作为弃用

// Function given a deck and number of players, will return all the players and the deck
let createPlayers deck numPlayers =

    // Function given a deck, will return the player and the new deck
    let createPlayer deck =
        let card, deck = drawCard deck ()
        let card', deck = drawCard deck ()
        { Hand = [card; card'] }, deck

    let dealToNewPlayer (players, deck) _ =
        let player, deck = createPlayer deck
        player :: players, deck

    [1..numPlayers] |> List.fold dealToNewPlayer ([], deck)

List.fold的类型为('State -> 'T -> 'State) -> 'State -> 'T list -> 'State。在这种情况下,因为我们使用类型为[1..numPlayers]的{​​{1}}启动折叠,所以泛型类型参数int list'T。因此,您需要定义int类型为folder的函数。

每次调用此函数时,您都希望累积状态,这意味着您要使用先前的'State -> 'int -> 'State值来计算deck的新值。您还希望获取生成的deck并添加到已生成的Player值列表中。这意味着您需要跟踪的状态必须包含两个 Player值列表和套牌。跟踪两者的最简单方法是通过元组 - 例如Player。这意味着您的Player list * Card list函数必须具有folder类型。

上面的Player list * Card list -> int -> Player list * Card list函数的类型为dealToNewPlayer,因为它忽略了第二个参数。因为它是通用的,所以它也适合类型Player list * Card list -> 'a -> Player list * Card list

您需要Player list * Card list -> int -> Player list * Card list的另一个参数是初始状态:List.fold。该值是一个元组,其中第一个元素是([], deck)值的空列表,第二个元素是完整的牌组。它符合状态类型Player

此特定Player list * Card list的返回值是累计状态,即List.fold。因此,整个Player list * Card list类型为createPlayers

以下是使用此功能的FSI会话示例:

Card list -> int -> Player list * Card list

正如您所看到的,它从输入> let deck = List.init 10 (fun i -> { Value = i });; val deck : Card list = [{Value = 0;}; {Value = 1;}; {Value = 2;}; {Value = 3;}; {Value = 4;}; {Value = 5;}; {Value = 6;}; {Value = 7;}; {Value = 8;}; {Value = 9;}] > createPlayers deck 3;; val it : Player list * Card list = ([{Hand = [{Value = 4;}; {Value = 5;}];}; {Hand = [{Value = 2;}; {Value = 3;}];}; {Hand = [{Value = 0;}; {Value = 1;}];}], [{Value = 6;}; {Value = 7;}; {Value = 8;}; {Value = 9;}]) 处理到3个玩家,因此返回的第一个元素是deck值列表,其中卡片发给每个玩家。元组的第二个元素包含剩余的套牌。