Delphi(pascal的扩展)一直有强大的类型系统。看看下面的实现简单的插入排序算法的示例代码(我使用Delphi 2009年):
01 unit InsSort;
02
03 interface
04
05 type
06 TItem = Integer;
07 TArray = array of TItem;
08
09 procedure InsertionSort(var A: TArray);
10
11 implementation
12
13 procedure InsertionSort(var A: TArray);
14 var
15 I, J: Integer;
16 Item: TItem;
17
18 begin
19 for I:= 1 + Low(A) to High(A) do begin
20 Item:= A[I];
21 J:= I - 1;
22 while (J >= Low(A)) and (A[J] > Item) do begin
23 A[J + 1]:= A[J];
24 Dec(J);
25 end;
26 A[J + 1]:= Item;
27 end;
28 end;
上面的代码对整数数组进行排序,如果我们需要处理字符串数组,我们所要做的就是把TItem声明从整数型转换为字
符串:
1 type
2 TItem = string;
如果我们的任务是进行整数和字符串数组排序,我们应该有两个单独的源代码单元(虽然他们只有一个词是不同的)。显然,这是重复的代码,不能用纯pascal解决。但是,Delphi有泛型,让我们用泛型来试试。
1 procedure InsertionSort<TItem>(var A: array of TItem);
先不要编译,如果我们需要使用泛型类,我们还需要一些手段比较泛型类型,所以我写了下面的简单泛型类:
01 type
02 TArray<TItem> = class
03 class function Compare(const L, R: TItem): Integer; static;
04 class procedure InsertionSort(var A: array of TItem); static;
05 end;
06
07 implementation
08
09 uses SysUtils, TypInfo;
10
11 class function TArray<TItem>.Compare(const L, R: TItem): Integer;
12 var
13 P: PTypeInfo;
14
15 begin
16 P:= TypeInfo(TItem);
17 case P^.Kind of
18 tkInteger: begin
19 if PInteger(@L)^ < PInteger(@R)^ then Result:= -1
20 else if PInteger(@L)^ > PInteger(@R)^ then Result:= 1
21 else Result:= 0;
22 end;
23 tkUString: Result:= CompareStr(PString(@L)^, PString(@R)^);
24 end;
25 end;
26
27 class procedure TArray<TItem>.InsertionSort(var A: array of TItem);
28 var
29 I, J: Integer;
30 Item: TItem;
31
32 begin
33 for I:= 1 + Low(A) to High(A) do begin
34 Item:= A[I];
35 J:= I - 1;
36 // while (J >= Low(A)) and (A[J] > Item) do begin
37 while (J >= Low(A)) and (Compare(A[J], Item) > 0) do begin
38 A[J + 1]:= A[J];
39 Dec(J);
40 end;
41 A[J + 1]:= Item;
42 end;
43 end;
它能工作,不过它看起来很丑陋,如果考虑到泛型比较疯狂的开销的话,特别是像通常由普通汇编指令比较整数类型只用一个单一指令。 Delphi2009的RTL(Generics.Defaults.pas 单元文件)在IComparer泛型接口的基础上提供了更优雅(也更长)的解决方案的,但它归结到底还是使用一个低效率的运行时类型信息。
但是,为什么我们需要'比较'的功能呢?当编译器为泛型TItem类型生成代码时,编译器应该已经知道TItem的实际代替类型,编译器可以利用实际类型来生成更优雅的代码行
1 while .. (A[J] > Item) ..
效率远远高于下面这个:
1 while .. (Compare(A[J], Item) > 0) ..
泛型直接比较的思想还涉及到Haskell的'类型的类'的概念。在'类型类'跟OOP类有很大的不同。 在Haskell中每个类型是许多类型类的实例。例如整数类型是'Eq'类型类的实例,因为整数可以比较相等,也是'ord'类型类的实例,因为所有的比较操作(<,<=,>,> =)适用于整数,也是'Show'类型类,因为整数可以转换为字符串,等等.在我们的情况下,泛型TItem类型应该是一个'ord'类型类的实例。
网友在我以前的贴子评论中指出,我的泛型排序简单例程的TArrayRTL代码包含额外的开销,(Generic.Collections & Generic.Defaults 单元文件),因为是循环内使用RTTI。是的,这是真的。让我们改进RTTI的检查,采取循环外代码,同时保持代码简单(不使用复杂的IComparer接口)。
简单的任务,应该有简单的解决方案。现在,我的解决办法似乎很简单,我也花了几个小时才找到它 -泛型对初学者来说仍然很怪异。最后一步,让系统工作的代码是 在泛型类TArray里面 声明一个泛型过程类型TCompare ,没有它的代码将不能编译:
08 | TCompare = function ( const L, R: T): Integer ; |
10 | class procedure InternalSort( var A: array of T; |
11 | Compare: TCompare); static; |
13 | class procedure InsertionSort( var A: array of T); static; |
16 | function CompareInt( const L, R: Integer ): Integer ; |
20 | uses SysUtils, TypInfo; |
22 | class procedure TArray . InternalSort( var A: array of T; Compare: TCompare); |
29 | for I:= 1 + Low(A) to High(A) do begin |
32 | while (J >= Low(A)) and (Compare(A[J], Item) > 0 ) do begin |
40 | function CompareInt( const L, R: Integer ): Integer ; |
42 | if L < R then Result:= - 1 |
43 | else if L > R then Result:= 1 |
47 | class procedure TArray . InsertionSort( var A: array of T); |
54 | tkInteger: InternalSort(A, @CompareInt); |
55 | tkUString: InternalSort(A, @CompareStr); |
请注意该'CompareInt'函数在接口部分声明。如果你注释掉接口声明编译会出错误