F#学习:理解什么是“可区分联合”(Discriminated Union)

// 下面首先定义了几个类型的简略形式(Type abbreviations),在编译时会被扩展为其真正的类型,
// 如下列的 int, string.
// 而其字面的缩略名称(Route, Make, Model)则可能消失。
type Route = int
type Make = string
type Model = string

// 这里定义了一个“可区分的联合”(Discriminated Union)
// 通过观察编译生成的代码发现:首先 Transport 被编译为了一个抽象类;
// 接下来,Car, Bicycle, Bus 被做成了内嵌在 Transport 中的,Transport 类的 public 子类。
// 并且,Transport 类含有一系列 static 的 factory method 用于创建几种不同类型的实例:
// Car, Bicycle, Bus.
// 
// 因此“可区分联合”大致上可以这样理解:它定义了几个事务的类型及其父子层次关系(使其可“区分”)
//
// Transport 是一个抽象类型;
// Car, Bicycle, Bus 都属于 Transport 类型,但是是它的子类
// 其含有的属性集合,构造器也各有不同。比如 Car 包含两个 string 属性 (Make 和 Model).
// Bus 包含一个 int 型的属性
// 而 Bicycle 则是一个简单的标记性的子类,不具有数据成员,只是用于区分不同的子类型而已。
type Transport = 
    | Car of Make * Model
    | Bicycle
    | Bus of Route

let nick = Car("BMW", "360")
// 定义一个列表,其第一个元素是 Bicycle, 第二个是 Bus. 列表类型可以理解为是 List<Transport>
let don = [ Bicycle; Bus 8 ]
// 类似上面。
let james = [ Car ("Ford", "Fiesta"); Bicycle ]

// 进行具体类型判断的模式匹配
// 由于这里只用了最简单的“类型判断”的模式匹配语法,实际编译后
// 会被简单的翻译为类似 C# 代码的:
// if (x is Car) {
//   ...
// } else if (x is Bicycle) {
//   ..
// }
// ...
// 当然,模式匹配是很强大的,并不限于类型判断。这只是最简单的一种情况而已。
match nick with
    | Car _ -> printfn "Car"
    | Bicycle -> printfn "Bicycle"
    | Bus _ -> printfn "Bus"

System.Console.ReadLine() |> ignore

 

可区分的联合也可以含有成员 (member). 如下列代码示例给二叉树添加了一个 Size 属性:

type Tree<'T> =
    | Node of 'T * Tree<'T> * Tree<'T>
    | Tip

    member t.Size =
        match t with
            | Node(_, l, r) -> 1 + l.Size + r.Size
            | Tip -> 0

let t: Tree<char> = Node('a', Node('b', Tip, Node('c', Tip, Node('d', Tip, Tip))), Tip)
printfn "%d" t.Size

System.Console.ReadLine() |> ignore

以上用了模式匹配的语法来计算节点数(忽略了 Tip)。即:

二叉树树的节点数 = 1(当前节点)+ l.Size (左子树节点数) + r.Size (右子树节点数)

这里显然用了一个递归,我们用 Reflector 看看编译生成的代码:

public int Size
{
    get
    {
        if (this is _Tip<T>)
        {
            return 0;
        }
        Node<T> node = (Node<T>) this;
        Program.Tree<T> r = node.item3;
        Program.Tree<T> l = node.item2;
        return ((1 + l.Size) + r.Size);
    }
}
 

可以发现该代码仍然使用的递归,而没有做任何优化(大概是因为该算法不是尾递归所以无法做自动优化)。

posted on 2010-07-28 00:50  NeilChen  阅读(1230)  评论(0编辑  收藏  举报

导航