[英]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]
折叠,因此通用类型参数'T
为int
。 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.