// 下面首先定义了几个类型的简略形式(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); } }
可以发现该代码仍然使用的递归,而没有做任何优化(大概是因为该算法不是尾递归所以无法做自动优化)。