F#基本类型——Discriminated Unions

定义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

接下来,我们可以继续扩充它,来表示一整幅牌。在一副牌中,每张牌都都有四种类型。除了AJQK四张牌外,剩下的110都可以利用一个元组类型来关联四种牌型。则可以表示如下:

    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存在如下特性:

  1. 可实例化
  2. 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

原文地址:https://www.cnblogs.com/TianFang/p/1655226.html