简体   繁体   English

如何在F#中为绘图卡建模

[英]How to Model Drawing Cards in F#

I'm working on modeling how to draw a playing card from the deck and I came up with the following solution: 我正在研究如何从甲板上抽出纸牌的建模,并提出了以下解决方案:

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

The main problem with this solution is that I'm using a mutable field for the CardDrawer to get the modified deck back. 这种解决方案的主要问题是我在CardDrawer中使用了可变字段来获取修改后的卡座。 I feel like using a computational expression would work here, but I'm not sure on how to implement it. 我觉得在这里可以使用计算表达式,但是我不确定如何实现它。

Any thoughts/suggestions? 有什么想法/建议吗?

EDIT: Here's an alternative setup, but how would I have createPlayer know how to take the mutated output and turn it back into input? 编辑:这是一个替代设置,但是我如何让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)

You can do it as a fold : 您可以将其折叠起来

// 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 has the type ('State -> 'T -> 'State) -> 'State -> 'T list -> 'State . List.fold的类型为('State -> 'T -> 'State) -> 'State -> 'T list -> 'State In this case, because we kick off the fold with [1..numPlayers] , which has the type int list , the generic type argument 'T is int . 在这种情况下,由于我们使用类型为int list [1..numPlayers]折叠,因此通用类型参数'Tint Therefore, you need to define a folder function with the type 'State -> 'int -> 'State . 因此,您需要使用'State -> 'int -> 'State类型定义一个folder函数。

Each time this function is called, you want to accumulate the state, which means that you want to use the previous value of deck to calculate the new value of deck . 每次这个函数被调用时,你希望积累的状态,这意味着你要使用的前值deck计算的新的价值deck You also want to take the generated Player and add to a list of already generated Player values. 您还希望获取已生成的Player并将其添加到已生成的Player值的列表中。 This means that the state you need to keep track of must contain both a list of Player values, and the deck. 这意味着您需要跟踪的状态必须包含Player值列表,也包含卡片组。 The simplest way to keep track of both is via a tuple - for example Player list * Card list . 跟踪两者的最简单方法是通过一个元组-例如Player list * Card list This means that your folder function must have the type Player list * Card list -> int -> Player list * Card list . 这意味着您的folder功能必须具有“ Player list * Card list -> int -> Player list * Card list

The above dealToNewPlayer function has the type Player list * Card list -> 'a -> Player list * Card list , because it ignores the second argument. 上面的dealToNewPlayer函数的类型为Player list * Card list -> 'a -> Player list * Card list dealToNewPlayer Player list * Card list -> 'a -> Player list * Card list ,因为它忽略了第二个参数。 Because it's generic, it also fits the type Player list * Card list -> int -> Player list * Card list . 因为它是通用的,所以它也适合类型Player list * Card list -> int -> Player list * Card list

The other argument to List.fold you need is the initial state: ([], deck) . 您所需的List.fold的另一个参数是初始状态: ([], deck) This value is a tuple where the first element is an empty list of Player values, and the second element is the full deck. 该值是一个元组,其中第一个元素是Player值的空列表,第二个元素是完整的牌组。 It fits the state type of Player list * Card list . 它适合Player list * Card list的状态类型。

The return value of this particular List.fold is the accumulated state, that is Player list * Card list . 该特定List.fold的返回值是累积状态,即Player list * Card list Thus, the entire type of createPlayers is Card list -> int -> Player list * Card list . 因此, createPlayers的整个类型是Card list -> int -> Player list * Card list createPlayers Card list -> int -> Player list * Card list createPlayers Card list -> int -> Player list * Card list

Here's an example of an FSI session that uses this function: 这是使用此功能的FSI会话的示例:

> 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;}])

As you can see, it deals from the input deck to 3 players, so the first element returned is a list of Player values, with the cards dealt to each player. 如您所见,它从输入deck到3个玩家进行交易,因此返回的第一个元素是Player值列表,并发给每个玩家纸牌。 The second element of the tuple contains the remaining deck. 元组的第二个元素包含剩余的卡片组。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM