第11课|枚举与布尔类型

123 阅读10分钟

本课目标

完成本课后,你将能够:

  • 深入理解枚举类型的定义、属性与内部表示
  • 掌握布尔类型的逻辑运算与短路控制
  • 熟练使用字符类型及其分类与转换
  • 应用枚举类型实现状态机与选择控制
  • 理解枚举类型的输入输出与表示子句

一、枚举类型基础

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超宽字符完整 Unicode32位

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)
  • 枚举类型天然适合实现状态机,结合数组可实现清晰的状态转换表

八、课后练习

  1. 枚举属性练习:定义枚举类型 Month(包含12个月),编写程序输出每个月的名称、位置序号,以及每个月的后继和前驱。
  2. 自定义表示:为 Month 类型指定表示值,使 January=1, February=2, ... December=12,并使用 'Enum_Rep 验证(如果编译器支持Ada 2022)。
  3. 布尔逻辑练习:编写函数实现“异或”逻辑的真值表验证,测试 A xor B 与 (A and not B) or (not A and B) 是否等价。
  4. 短路求值应用:编写一个函数,接受一个整数数组和一个索引,安全地检查索引是否在范围内且对应元素大于0。使用 and then 实现。
  5. 状态机扩展:修改交通灯示例,增加“闪烁黄灯”状态,并定义相应的转换逻辑。
  6. 字符处理:编写程序读取一行文本,统计其中的字母、数字、空格和其他字符的数量。

九、下节预告

第12课|数组类型详解

我们将:

  • 掌握约束数组与非约束数组的定义与使用
  • 学习多维数组与数组切片操作
  • 理解数组属性与数组运算
  • 应用数组解决批量数据处理问题

关键术语表

枚举类型:将可能的值以标识符列表形式定义的离散类型

位置编号:枚举值在声明中的序号,从0开始,可通过 'Pos 获取

表示值:枚举值在内存中的实际整数值,可通过表示子句自定义

布尔类型:预定义的枚举类型 (False, True),用于逻辑判断

逻辑运算符:对布尔值进行逻辑运算的操作符(and, or, xor, not)

短路求值and then / or else 运算符,保证从左到右求值并在结果确定时停止

Latin-1:ISO-8859-1 字符集,Character 类型使用的标准字符集

UCS-2 / UnicodeWide_Character 和 Wide_Wide_Character 支持的字符集

状态机:基于有限状态集的系统模型,可用枚举类型优雅实现