本课目标
完成本课后,你将能够:
- 深入理解枚举类型的定义、属性与内部表示
- 掌握布尔类型的逻辑运算与短路控制
- 熟练使用字符类型及其分类与转换
- 应用枚举类型实现状态机与选择控制
- 理解枚举类型的输入输出与表示子句
一、枚举类型基础
1.1 枚举类型的定义
枚举类型是将所有可能的值以标识符形式列出的离散类型:
-- 基本枚举定义
type Day is (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
type Color is (Red, Orange, Yellow, Green, Blue, Indigo, Violet);
type Status is (Idle, Running, Stopped, Error);
-- 单字符枚举(带引号)
type Hex_Char is ('A', 'B', 'C', 'D', 'E', 'F');
-- 使用枚举变量
Today : Day := Wed;
Current_Color : Color := Green;
枚举类型的特点:
- 值是离散的、可枚举的
- 每个值都有明确的前驱和后继
- 值的有序性由声明顺序决定
1.2 枚举值的内部表示
-- 默认表示:Red=0, Green=1, Blue=2
type Tricolor is (Red, Green, Blue);
-- 可通过属性查询位置
Red_Pos : constant Integer := Tricolor'Pos (Red); -- 0
Green_Pos : constant Integer := Tricolor'Pos (Green); -- 1
Blue_Pos : constant Integer := Tricolor'Pos (Blue); -- 2
-- 反向转换:从位置到枚举值
Color_Value : constant Tricolor := Tricolor'Val (1); -- Green
1.3 自定义枚举表示
可以使用表示子句为枚举值指定特定的整数值:
type Tricolor is (Red, Green, Blue);
-- 自定义表示值(十六进制颜色代码)
for Tricolor use (
Red => 16#FF_00_00_00#,
Green => 16#00_FF_00_00#,
Blue => 16#00_00_FF_00#
);
-- 指定存储大小(位)
for Tricolor'Size use 32;
-- Ada 2022 新增:查询表示值
Red_Rep : constant Integer := Tricolor'Enum_Rep (Red); -- 4278190080
1.4 枚举子类型
可以从枚举类型创建子类型,限制取值范围:
type Day is (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
subtype Weekday is Day range Mon .. Fri; -- 仅工作日
subtype Weekend is Day range Sat .. Sun; -- 仅周末
Work_Day : Weekday := Wed; -- OK
-- Work_Day := Sat; -- 错误!Sat不在Weekday范围内
二、枚举类型的属性
枚举类型(及所有离散类型)提供以下属性:
| 属性 | 含义 | 示例 |
|---|---|---|
'First | 第一个枚举值 | Day'First = Mon |
'Last | 最后一个枚举值 | Day'Last = Sun |
'Range | 取值范围 | Day'Range = Mon .. Sun |
'Pos (X) | 枚举值的位置(从0开始) | Day'Pos (Wed) = 2 |
'Val (N) | 位置N对应的枚举值 | Day'Val (2) = Wed |
'Succ (X) | 下一个枚举值 | Day'Succ (Wed) = Thu |
'Pred (X) | 前一个枚举值 | Day'Pred (Wed) = Tue |
'Image (X) | 枚举值的字符串表示 | Day'Image (Mon) = "MON" |
'Value (S) | 字符串转枚举值 | Day'Value ("MON") = Mon |
'Enum_Rep (X) | 表示值(Ada 2022) | Tricolor'Enum_Rep (Red) |
'Enum_Val (N) | 表示值转枚举(Ada 2022) | Tricolor'Enum_Val (16#FF#) |
with Ada.Text_IO;
procedure Enum_Attributes is
type Day is (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
Today : Day := Wed;
begin
Ada.Text_IO.Put_Line ("First day: " & Day'Image (Day'First));
Ada.Text_IO.Put_Line ("Last day: " & Day'Image (Day'Last));
Ada.Text_IO.Put_Line ("Today's position: " & Integer'Image (Day'Pos (Today)));
Ada.Text_IO.Put_Line ("Tomorrow: " & Day'Image (Day'Succ (Today)));
Ada.Text_IO.Put_Line ("Yesterday: " & Day'Image (Day'Pred (Today)));
end Enum_Attributes;
三、枚举类型的输入输出
枚举类型的输入输出需要通过实例化 Ada.Text_IO.Enumeration_IO 泛型包来实现:
with Ada.Text_IO;
procedure Enum_IO_Example is
type Day is (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
-- 实例化枚举IO包
package Day_IO is new Ada.Text_IO.Enumeration_IO (Day);
use Day_IO;
Today : Day := Wed;
begin
-- 输出枚举值
Ada.Text_IO.Put ("Today is ");
Put (Today); -- 输出: WED
Ada.Text_IO.New_Line;
-- 控制输出格式:宽度、大小写
Ada.Text_IO.Put ("Padded: ");
Put (Today, Width => 8); -- 输出: " WED"
Ada.Text_IO.New_Line;
Ada.Text_IO.Put ("Lowercase: ");
Put (Today, Set => Lower_Case); -- 输出: "wed"
Ada.Text_IO.New_Line;
-- 从字符串读取枚举值(需先实例化)
-- Get (Today); -- 从标准输入读取
end Enum_IO_Example;
Enumeration_IO 提供的主要过程:
Put (Item : in Enum; Width : in Field := 0; Set : in Type_Set := Upper_Case)Get (Item : out Enum)— 从文件或标准输入读取Get (From : in String; Item : out Enum; Last : out Positive)— 从字符串读取
四、布尔类型
4.1 布尔类型的定义
布尔类型 Boolean 是预定义的枚举类型:
-- Boolean 的本质定义(Ada标准库中)
type Boolean is (False, True);
-- 这意味着 Boolean 拥有枚举类型的所有属性
B1 : Boolean := True;
B2 : Boolean := False;
-- 可以使用枚举属性
First_Bool : constant Boolean := Boolean'First; -- False
Last_Bool : constant Boolean := Boolean'Last; -- True
布尔类型的所有操作都遵循枚举类型的规则,FALSE的值为0,TRUE的值为1。
4.2 关系运算符
所有关系运算符的结果类型都是 Boolean:
A, B : Integer := 10;
Result : Boolean;
Result := (A = B); -- 等于,返回 True
Result := (A /= B); -- 不等于,返回 False
Result := (A < B); -- 小于,返回 False
Result := (A <= B); -- 小于等于,返回 True
Result := (A > B); -- 大于,返回 False
Result := (A >= B); -- 大于等于,返回 True
4.3 逻辑运算符
布尔类型支持以下逻辑运算符:
| 运算符 | 含义 | 真值表 |
|---|---|---|
and | 逻辑与 | True and True = True,其余为 False |
or | 逻辑或 | False or False = False,其余为 True |
xor | 异或 | 相同为 False,不同为 True |
not | 逻辑非 | not True = False,not False = True |
procedure Boolean_Logic is
A, B, C : Boolean;
begin
A := True;
B := False;
C := A and B; -- False
C := A or B; -- True
C := A xor B; -- True
C := not A; -- False
-- 复合表达式
C := (A and B) or (not A and B); -- 复杂的逻辑组合
C := A xor B xor True; -- 异或链
end Boolean_Logic;
4.4 短路求值:and then 与 or else
Ada 提供短路形式的逻辑运算符,确保从左到右求值,并在结果确定时停止:
-- and then:第一个为 False 则不再求第二个
if K /= 0 and then 1.0 / Float (K) > 0.5 then
-- 安全:当 K=0 时不会计算除法
end if;
-- or else:第一个为 True 则不再求第二个
if Index > Max_Index or else Array (Index) = 0 then
-- 安全:Index 超出范围时不会访问数组
end if;
短路求值的意义:
- 安全性:防止对无效表达式求值(如空指针、除零、越界)
- 副作用控制:当第二个表达式有副作用时,可控制其是否执行
- 性能:理论上可加速,但现代编译器优化后差别不大,主要用途是安全
重要区别:
and/or:可能求值所有操作数(编译器可优化,但不保证顺序)and then/or else:保证从左到右求值,结果确定即停止
4.5 布尔类型的输入输出
与枚举类型一样,需要实例化 Enumeration_IO:
with Ada.Text_IO;
procedure Bool_IO_Example is
package Bool_IO is new Ada.Text_IO.Enumeration_IO (Boolean);
use Bool_IO;
Flag : Boolean := True;
begin
Put ("Flag = ");
Put (Flag); -- 输出: TRUE
Ada.Text_IO.New_Line;
Put (Flag, Width => 5); -- 输出: " TRUE"
Ada.Text_IO.New_Line;
Put (Flag, Set => Lower_Case); -- 输出: "true"
Ada.Text_IO.New_Line;
end Bool_IO_Example;
五、字符类型
5.1 字符类型的层次
Ada 提供三级字符类型,对应不同的 Unicode 支持:
| 类型 | 说明 | 字符集 | 存储 |
|---|---|---|---|
Character | 基本字符类型 | Latin-1(ISO-8859-1) | 8位 |
Wide_Character | 宽字符 | Unicode BMP(UCS-2) | 16位 |
Wide_Wide_Character | 超宽字符 | 完整 Unicode | 32位 |
5.2 字符字面量与属性
with Ada.Text_IO;
procedure Char_Demo is
Ch : Character := 'A';
Digit : Character := '7';
Space : Character := ' ';
Newline : constant Character := ASCII.LF; -- 控制字符
Wch : Wide_Character := '中'; -- 中文字符
WWch : Wide_Wide_Character := '☯'; -- Emoji
begin
-- 字符属性
Ada.Text_IO.Put_Line ("Ch pos: " & Integer'Image (Character'Pos (Ch)));
Ada.Text_IO.Put_Line ("Ch succ: " & Character'Image (Character'Succ (Ch)));
-- 判断字符类型(需 Ada.Characters.Handling)
-- Is_Digit, Is_Letter, Is_Lower, Is_Upper 等函数
end Char_Demo;
5.3 字符分类与转换
Ada 提供 Ada.Characters.Handling 包进行字符操作:
with Ada.Characters.Handling;
with Ada.Text_IO;
procedure Char_Handling is
use Ada.Characters.Handling;
Ch : Character := 'a';
begin
if Is_Letter (Ch) then
Ada.Text_IO.Put_Line (Ch & " is a letter");
end if;
if Is_Digit ('7') then
Ada.Text_IO.Put_Line ("7 is a digit");
end if;
-- 大小写转换
Upper := To_Upper (Ch); -- 'A'
Lower := To_Lower ('Z'); -- 'z'
-- 其他分类
Is_Control (ASCII.ESC); -- 控制字符
Is_Space (' '); -- 空白字符
Is_Punctuation (','); -- 标点符号
end Char_Handling;
5.4 宽字符支持
with Ada.Wide_Text_IO;
with Ada.Wide_Wide_Text_IO;
procedure Wide_Char_Demo is
W_Str : Wide_String := "Hello 世界";
WW_Str : Wide_Wide_String := "☯ 阴阳";
begin
Ada.Wide_Text_IO.Put_Line (W_Str);
Ada.Wide_Wide_Text_IO.Put_Line (WW_Str);
end Wide_Char_Demo;
六、完整示例:状态机实现
-- 文件名: traffic_light.adb
-- 功能:用枚举类型实现交通信号灯状态机
with Ada.Text_IO;
with Ada.Calendar;
procedure Traffic_Light is
-- 交通灯状态枚举
type Light_State is (Red, Red_Yellow, Green, Yellow);
-- 状态持续时间(秒)
type Duration_Array is array (Light_State) of Duration;
State_Duration : constant Duration_Array :=
(Red => 30.0, Red_Yellow => 3.0, Green => 25.0, Yellow => 3.0);
-- 状态转换表:每个状态的下一个状态
Next_State : constant array (Light_State) of Light_State :=
(Red => Red_Yellow, Red_Yellow => Green, Green => Yellow, Yellow => Red);
-- 状态名称字符串(用于输出)
function State_Name (S : Light_State) return String is
begin
case S is
when Red => return "红灯";
when Red_Yellow => return "红黄灯";
when Green => return "绿灯";
when Yellow => return "黄灯";
end case;
end State_Name;
-- 实例化枚举IO(用于调试输出英文名)
package State_IO is new Ada.Text_IO.Enumeration_IO (Light_State);
Current_State : Light_State := Red;
Start_Time : Ada.Calendar.Time;
Elapsed : Duration;
begin
Ada.Text_IO.Put_Line ("交通信号灯模拟开始(按Ctrl+C退出)");
Ada.Text_IO.New_Line;
loop
-- 记录状态开始时间
Start_Time := Ada.Calendar.Clock;
-- 输出当前状态
Ada.Text_IO.Put ("当前状态: ");
Ada.Text_IO.Put (State_Name (Current_State));
Ada.Text_IO.Put (" (");
State_IO.Put (Current_State, Set => Lower_Case);
Ada.Text_IO.Put (") 持续时间: ");
Ada.Text_IO.Put (Duration'Image (State_Duration (Current_State)));
Ada.Text_IO.Put_Line (" 秒");
-- 等待当前状态持续时间
loop
Elapsed := Ada.Calendar.Clock - Start_Time;
exit when Elapsed >= State_Duration (Current_State);
-- 每秒输出一次倒计时
delay 1.0;
Ada.Text_IO.Put (" 剩余: ");
Ada.Text_IO.Put (Integer'Image (Integer (State_Duration (Current_State) - Elapsed)));
Ada.Text_IO.Put ("秒 ");
Ada.Text_IO.New_Line;
end loop;
-- 切换到下一个状态
Current_State := Next_State (Current_State);
Ada.Text_IO.New_Line;
end loop;
end Traffic_Light;
七、本课总结
- 枚举类型是离散类型的核心,将值以标识符形式列出,支持丰富的属性
- 枚举值的内部表示默认从0开始,可通过表示子句自定义
- 枚举类型的输入输出需实例化
Enumeration_IO泛型包 - 布尔类型
Boolean是预定义的枚举类型(False, True) - 布尔运算包括关系运算符(=、/=、<、<=、>、>=)和逻辑运算符(and、or、xor、not)
- 短路运算符
and then/or else保证从左到右求值,用于安全控制 - 字符类型分三级:
Character(Latin-1)、Wide_Character(BMP)、Wide_Wide_Character(Unicode) - 枚举类型天然适合实现状态机,结合数组可实现清晰的状态转换表
八、课后练习
- 枚举属性练习:定义枚举类型
Month(包含12个月),编写程序输出每个月的名称、位置序号,以及每个月的后继和前驱。 - 自定义表示:为
Month类型指定表示值,使 January=1, February=2, ... December=12,并使用'Enum_Rep验证(如果编译器支持Ada 2022)。 - 布尔逻辑练习:编写函数实现“异或”逻辑的真值表验证,测试
A xor B与(A and not B) or (not A and B)是否等价。 - 短路求值应用:编写一个函数,接受一个整数数组和一个索引,安全地检查索引是否在范围内且对应元素大于0。使用
and then实现。 - 状态机扩展:修改交通灯示例,增加“闪烁黄灯”状态,并定义相应的转换逻辑。
- 字符处理:编写程序读取一行文本,统计其中的字母、数字、空格和其他字符的数量。
九、下节预告
第12课|数组类型详解
我们将:
- 掌握约束数组与非约束数组的定义与使用
- 学习多维数组与数组切片操作
- 理解数组属性与数组运算
- 应用数组解决批量数据处理问题
关键术语表
枚举类型:将可能的值以标识符列表形式定义的离散类型
位置编号:枚举值在声明中的序号,从0开始,可通过
'Pos获取表示值:枚举值在内存中的实际整数值,可通过表示子句自定义
布尔类型:预定义的枚举类型
(False, True),用于逻辑判断逻辑运算符:对布尔值进行逻辑运算的操作符(and, or, xor, not)
短路求值:
and then/or else运算符,保证从左到右求值并在结果确定时停止Latin-1:ISO-8859-1 字符集,
Character类型使用的标准字符集UCS-2 / Unicode:
Wide_Character和Wide_Wide_Character支持的字符集状态机:基于有限状态集的系统模型,可用枚举类型优雅实现