Delphi泛型

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 ,没有它的代码将不能编译:

01 unit GenericSort;
02  
03 interface
04  
05 type
06   TArray<T> = class
07   public type
08     TCompare = function(const L, R: T): Integer;
09   private
10     class procedure InternalSort(var A: array of T;
11       Compare: TCompare); static;
12   public
13     class procedure InsertionSort(var A: array of T); static;
14   end;
15  
16 function CompareInt(const L, R: Integer): Integer;
17  
18 implementation
19  
20 uses SysUtils, TypInfo;
21  
22 class procedure TArray.InternalSort(var A: array of T; Compare: TCompare);
23 var
24   I, J: Integer;
25   Item: T;
26   P: PTypeInfo;
27  
28 begin
29   for I:= 1 + Low(A) to High(A) do begin
30     Item:= A[I];
31     J:= I - 1;
32     while (J >= Low(A)) and (Compare(A[J], Item) > 0do begin
33       A[J + 1]:= A[J];
34       Dec(J);
35     end;
36     A[J + 1]:= Item;
37   end;
38 end;
39  
40 function CompareInt(const L, R: Integer): Integer;
41 begin
42  if L < R then Result:= -1
43   else if L > R then Result:= 1
44   else Result:= 0;
45 end;
46  
47 class procedure TArray.InsertionSort(var A: array of T);
48 var
49   P: PTypeInfo;
50  
51 begin
52   P:= TypeInfo(T);
53   case P^.Kind of
54     tkInteger: InternalSort(A, @CompareInt);
55     tkUString: InternalSort(A, @CompareStr);
56   end;
57 end;
58  
59 end.

请注意该'CompareInt'函数在接口部分声明。如果你注释掉接口声明编译会出错误


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值