本课目标
完成本课后,你将能够:
- 理解泛型的概念与代码复用的优势
- 掌握泛型包的声明与实例化
- 学会泛型子程序的编写
- 应用泛型实现通用容器(如栈、队列)
- 理解泛型参数的不同种类(类型、值、子程序)
一、泛型的基本概念
1.1 为什么需要泛型
在强类型语言中,为每种数据类型编写相同的算法是重复劳动。泛型(generic)允许我们编写与类型无关的代码,在编译时实例化为具体类型。
-- 没有泛型:为每种类型写一个栈
package Int_Stack is ... -- 整数栈
package Float_Stack is ... -- 浮点栈
-- 代码重复!
-- 使用泛型:一次编写,多种使用
generic
type Element_Type is private;
package Generic_Stack is ... -- 通用栈
1.2 泛型的形式
Ada 的泛型通过 generic 关键字声明,可以参数化:
- 类型参数:如
type T is private - 值参数:如
Size : Positive - 子程序参数:如
with function "+" (A, B : T) return T
-- 泛型包声明
generic
type Item is private;
Max_Size : Positive := 100; -- 默认值
package Stack is
procedure Push (X : Item);
procedure Pop (X : out Item);
function Is_Empty return Boolean;
end Stack;
二、泛型包
2.1 声明泛型包
-- 文件名:generic_stack.ads
generic
type Element is private;
Capacity : Positive := 10;
package Generic_Stack is
procedure Push (E : Element);
procedure Pop (E : out Element);
function Is_Empty return Boolean;
function Is_Full return Boolean;
Stack_Empty : exception;
Stack_Full : exception;
end Generic_Stack;
2.2 实现泛型包体
-- 文件名:generic_stack.adb
package body Generic_Stack is
type Array_Type is array (1 .. Capacity) of Element;
Data : Array_Type;
Top : Natural := 0;
procedure Push (E : Element) is
begin
if Is_Full then
raise Stack_Full;
end if;
Top := Top + 1;
Data (Top) := E;
end Push;
procedure Pop (E : out Element) is
begin
if Is_Empty then
raise Stack_Empty;
end if;
E := Data (Top);
Top := Top - 1;
end Pop;
function Is_Empty return Boolean is (Top = 0);
function Is_Full return Boolean is (Top = Capacity);
end Generic_Stack;
2.3 实例化泛型包
使用 new 关键字创建具体包:
with Generic_Stack;
procedure Use_Stack is
-- 实例化为整数栈,容量 20
package Int_Stack is new Generic_Stack (Element => Integer, Capacity => 20);
-- 实例化为浮点栈,使用默认容量 10
package Float_Stack is new Generic_Stack (Element => Float);
X : Integer;
begin
Int_Stack.Push (42);
Int_Stack.Pop (X);
Ada.Text_IO.Put_Line (Integer'Image (X));
end Use_Stack;
三、泛型子程序
3.1 泛型函数
generic
type T is private;
with function ">" (A, B : T) return Boolean is <>; -- 默认使用内置比较
function Generic_Max (A, B : T) return T;
-- 实现
function Generic_Max (A, B : T) return T is
begin
if A > B then
return A;
else
return B;
end if;
end Generic_Max;
3.2 实例化泛型函数
function Max_Int is new Generic_Max (Integer);
function Max_Float is new Generic_Max (Float);
-- 自定义类型需要提供比较函数
type Person is record ...;
function ">" (P1, P2 : Person) return Boolean is ...;
function Max_Person is new Generic_Max (Person);
四、泛型参数详解
4.1 类型参数的种类
| 形式 | 含义 | 允许的操作 |
|---|---|---|
type T is private | 任意非受限类型 | 赋值、相等比较 |
type T is limited private | 受限类型 | 无赋值和相等 |
type T (D : Discrete) is limited private | 带判别式的类型 | - |
type T is range <> | 整数类型 | 算术运算、比较 |
type T is digits <> | 浮点类型 | 浮点运算 |
type T is delta <> | 定点类型 | 定点运算 |
type T is (<>); | 离散类型(枚举或整数) | 枚举属性、比较 |
4.2 值参数
generic
Size : Positive; -- 编译时常量
Initial : Integer := 0; -- 默认值
package Array_Utils is
type My_Array is array (1 .. Size) of Integer;
procedure Fill (A : out My_Array; Value : Integer := Initial);
end Array_Utils;
4.3 子程序参数
generic
type T is private;
with function Add (A, B : T) return T;
with function Zero return T;
function Generic_Sum (Items : array (Positive range <>) of T) return T;
-- 实现
function Generic_Sum (Items : array (Positive range <>) of T) return T is
Result : T := Zero;
begin
for I in Items'Range loop
Result := Add (Result, Items (I));
end loop;
return Result;
end Generic_Sum;
实例化:
function Sum_Int is new Generic_Sum (Integer, Add => "+", Zero => 0);
function Sum_String is new Generic_Sum (String, Add => "&", Zero => "");
五、完整示例:通用动态数组
-- 文件名:generic_vector.ads
-- 泛型动态数组(类似 C++ vector)
generic
type Element is private;
Initial_Capacity : Positive := 16;
package Generic_Vector is
type Vector is private;
procedure Append (V : in out Vector; E : Element);
procedure Pop (V : in out Vector; E : out Element);
function Length (V : Vector) return Natural;
function Is_Empty (V : Vector) return Boolean;
function Get (V : Vector; Index : Positive) return Element;
procedure Set (V : in out Vector; Index : Positive; E : Element);
procedure Clear (V : in out Vector);
Index_Error : exception;
private
type Element_Array is array (Positive range <>) of Element;
type Vector is record
Data : Element_Array (1 .. Initial_Capacity);
Len : Natural := 0;
end record;
end Generic_Vector;
-- 文件名:generic_vector.adb
with Ada.Unchecked_Deallocation;
package body Generic_Vector is
procedure Grow (V : in out Vector) is
New_Cap : Positive := V.Data'Length * 2;
New_Data : Element_Array (1 .. New_Cap);
begin
New_Data (1 .. V.Len) := V.Data (1 .. V.Len);
V.Data := New_Data;
end Grow;
procedure Append (V : in out Vector; E : Element) is
begin
if V.Len = V.Data'Length then
Grow (V);
end if;
V.Len := V.Len + 1;
V.Data (V.Len) := E;
end Append;
procedure Pop (V : in out Vector; E : out Element) is
begin
if V.Len = 0 then
raise Index_Error;
end if;
E := V.Data (V.Len);
V.Len := V.Len - 1;
end Pop;
function Length (V : Vector) return Natural is (V.Len);
function Is_Empty (V : Vector) return Boolean is (V.Len = 0);
function Get (V : Vector; Index : Positive) return Element is
begin
if Index > V.Len then
raise Index_Error;
end if;
return V.Data (Index);
end Get;
procedure Set (V : in out Vector; Index : Positive; E : Element) is
begin
if Index > V.Len then
raise Index_Error;
end if;
V.Data (Index) := E;
end Set;
procedure Clear (V : in out Vector) is
begin
V.Len := 0;
end Clear;
end Generic_Vector;
-- 文件名:main.adb
with Generic_Vector;
with Ada.Text_IO;
procedure Main is
package Int_Vector is new Generic_Vector (Element => Integer);
use Int_Vector;
V : Vector;
X : Integer;
begin
-- 添加元素
for I in 1 .. 10 loop
Append (V, I * 10);
end loop;
Ada.Text_IO.Put_Line ("Length: " & Natural'Image (Length (V)));
-- 访问元素
for I in 1 .. Length (V) loop
Ada.Integer_Text_IO.Put (Get (V, I), Width => 3);
end loop;
Ada.Text_IO.New_Line;
-- 弹出元素
while not Is_Empty (V) loop
Pop (V, X);
Ada.Integer_Text_IO.Put (X, Width => 3);
end loop;
Ada.Text_IO.New_Line;
end Main;
六、本课总结
- 泛型允许编写与类型无关的代码,通过实例化生成具体版本
- 泛型包使用
generic声明,通过new实例化 - 泛型参数可以是类型、值或子程序
- 类型参数可用
private、limited private、range <>、digits <>、delta <>、(<>)等约束 - 子程序参数可指定默认操作(如
is <>使用内置运算符) - 泛型是实现容器库(栈、向量、列表)的基础
七、课后练习
-
泛型交换:编写泛型过程
Swap,交换两个任意类型的变量。 -
泛型栈扩展:为
Generic_Stack添加Peek函数(查看栈顶但不弹出)。 -
泛型队列:实现一个泛型循环队列(FIFO),支持
Enqueue、Dequeue、Is_Empty、Is_Full。 -
泛型查找:编写泛型函数
Find,在数组中查找给定元素,返回索引或 0。 -
泛型排序:实现泛型冒泡排序,接受比较函数参数,对任意数组排序。
八、下节预告
第21课|面向对象编程(标记记录)
我们将:
- 理解标记记录(tagged record)作为类的基础
- 掌握继承与多态的实现
- 学习动态分派与类宽类型
- 应用面向对象设计模式
关键术语表
泛型(generic):参数化的代码模板,在实例化时生成具体代码
实例化(instantiation):使用
new关键字将泛型转换为具体包或子程序类型参数:泛型中传递类型的形式参数
值参数:泛型中传递编译时常量的形式参数
子程序参数:泛型中传递子程序的形式参数
private:表示类型支持赋值和相等比较
limited private:表示类型不支持赋值和相等比较
<>:表示未指定约束,常用于离散类型或形式化子程序默认
is <>:表示使用预定义的运算符作为默认子程序参数