F#建模纸牌游戏

6

我正在尝试在F#中表示标准扑克牌。我的目标是实现 Microsoft Solitaire的克隆版(附带于Windows),这个游戏中花色、面值和颜色是重要的。此练习主要旨在学习一些F#。

我考虑使用判别联合:

type Suit =
    | Diamonds
    | Hearts
    | Clubs
    | Spades

type Color =
    | Red
    | Black

type Face =
    Two | Three | Four | Five | Six | Seven |
    Eight | Nine | Ten | Jack | Queen | King | Ace

使用记录类型来表示卡片:

type Card = {
    suit: Suit;
    face: Face;
    color: Color;
}

然而,从花色可以推断出卡牌的颜色——所有方块和红桃都是红色的,所有梅花和黑桃都是黑色的。仅凭颜色无法确定花色。也许这样比较合适:
type Suit =
    | Diamonds of Color //should always be red
    | Hearts of Color //should always be red
    | Clubs of Color //should always be black
    | Spades of Color //should always be black

type Face =
    Two | Three | Four | Five | Six | Seven |
    Eight | Nine | Ten | Jack | Queen | King | Ace

type Card = {
    suit: Suit;
    face: Face;
}

但这似乎不正确,因为它允许不正确的组合,例如黑桃和红心。
我的问题是:
  1. 考虑到颜色依赖于花色,处理花色和颜色的最惯用方式是什么?
  2. 是否应明确表示颜色概念?理论上可以将所有颜色实例替换为钻石或红心(红色)和梅花或黑桃(黑色)的模式匹配。
2个回答

16

由于从 Suit 可以推断出 Color,因此没有必要明确地对其进行建模。你需要让不合法的状态不可表示。

使用一个活动模式(Active Pattern),您仍然可以获得良好的编程体验,并且有一种很好的方式来建模颜色:

type Suit =
    | Diamonds
    | Hearts
    | Clubs
    | Spades

let (|Red|Black|) suit =
    match suit with
    | Diamonds | Hearts -> Red
    | Clubs | Spades -> Black

这将使您能够对 Suit 进行模式匹配,例如下面这个愚蠢的例子:
let printColor card =
    match card.Suit with
    | Red -> "Red"
    | Black -> "Black"

FSI的使用示例:

> printColor { Suit = Spades; Face = Ace };;
val it : string = "Black"
> printColor { Suit = Diamonds; Face = King };;
val it : string = "Red"

1
活动模式非常酷且实用 :) 每当我看到它们的一个很棒的应用程序时,我都会惊叹于这样一个简单的机制如何在清理代码方面如此有用。 - wasatz

2
您可以添加一种录制方法:
type Card = 
    {suit: Suit;face: Face}
    member this.Color = 
        match this.suit with
            | Diamonds | Hearts -> Red
            | Clubs | Spades -> Black

例子:

let v = {suit = Diamonds;face = Two}
printfn "%A" v.Color

let v1 = {suit = Clubs;face = Two}
printfn "%A" v1.Color

红色 黑色 请按任意键继续...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接