定义Discriminated Unions:
Discriminated Unions是F#的一种特有的数据类型,其基本语法格式如下:
type type-name =
| case-identifier1 [of type1 [ * type2 ...]
| case-identifier2 [of type3 [ * type4 ...]
...
Discriminated Unions主要用来表示在一组具有同一类型的子类型(可以看做一组具有共同基类的子类)。例如,我们要表示一套扑克中的四种花色:红桃、方块、梅花、黑桃,可以表示如下:
type Suit =
| Heart
| Diamond
| Spade
| Club
接下来,我们可以继续扩充它,来表示一整幅牌。在一副牌中,每张牌都都有四种类型。除了A、J、Q、K四张牌外,剩下的1到10都可以利用一个元组类型来关联四种牌型。则可以表示如下:
type ValueCard =
| Ace of Suit
| King of Suit
| Queen of Suit
| Jack of Suit
| ValueCard of int * Suit
let deckOfCards =
[
for suit in [ Spade; Club; Heart; Diamond ] do
yield Ace(suit)
yield King(suit)
yield Queen(suit)
yield Jack(suit)
for value in 2 .. 10 do
yield ValueCard(value, suit)
]
使用Discriminated Unions:
Discriminated Unions经常和模式匹配一起使用,例如,我们定义扑克牌的出牌法则如下:
let describeHoleCards cards =
match cards with
| [] | [_]
-> failwith "Too few cards."
| cards when List.length cards > 2
-> failwith "Too many cards."
| [ Ace(_); Ace(_) ] -> "Pocket Rockets"
| [ King(_); King(_) ] -> "Cowboys"
| [ ValueCard(2, _); ValueCard(2, _)] -> "Ducks"
| [ Queen(_); Queen(_) ]
| [ Jack(_); Jack(_) ]
-> "Pair of face cards"
| [ ValueCard(x, _); ValueCard(y, _) ] when x = y
-> "A Pair"
| [ first; second ]
-> sprintf "Two cards: %A and %A" first second
非常优雅而强大,这是那些没有模式匹配的语言所无法比拟的。
用C#实现Discriminated Unions
正所谓条条大路通罗马,这种扑克牌的功能也可以用C#表示,首先拿花色来说,最直接的反应便是用枚举实现:
enum Suit { Heart, Diamond, Spade, Club }
然而这种方式存在一定隐患:我们可以很容易写出通过编译器检查的非法的花色
var invalidSuit1 = Suit.Club | Suit.Diamond;
var invalidSuite2 = (Suit) - 1;
要避免这种隐患,可以通过如下两种方式:
方式1:
class Suite
{
private Suite() { }
static Suite()
{
Heart = new Suite();
Diamond = new Suite();
Spade = new Suite();
Club = new Suite();
}
public static Suite Heart { get; private set; }
public static Suite Diamond { get; private set; }
public static Suite Spade { get; private set; }
public static Suite Club { get; private set; }
}
方式2:
abstract class Suite { }
class Heard : Suite { }
class Diamond : Suite { }
class Spade : Suite { }
class Club : Suite { }
由于F#和C#本身是两种不同特性的语言,没有必要把其实现和C#一一对应起来(虽然确实可以一一对应起来),我并没有通过Reflector来看其具体对应着C#的实现。仅仅从功能上来讲,这两种方式都实现了我们预期的功能。 但Discriminated Unions存在如下特性:
- 可实例化
- union case可以为不同的类型
根据这两个特性来看,方式1不满足这两个条件,而方式2基本上和Discriminated Unions是等价的。接下来定义ValueCard时,方式1就不能满足需求,而方式2却仍然适用。因此,就像我前面所说的那样,Discriminated Unions可以看做一组具有共同基类的子类。
由于C#没有模式匹配,在接下来扑克牌规则的实现时,C#只能通过无数的if来模拟这种功能,代码几乎是F#的三倍左右,并且可维护性非常差。在这方面,F#以其独有的语法特性占有绝对的优势。
参考文章:
http://blog.csdn.net/minyskirt/archive/2009/12/15/5010779.aspx