文章目录
- Ada 常用的库和方法
- Ada.Characters.Handling
- 字符类型函数
- 转换函数
- Ada 基础语法概览
- 数据类型和子类型
- 类型(Type)
- 子类型(Subtype)
- 类型和子类型的区别
- 常用类型转换方法
- 显示类型转换
- 类型相关函数
- 循环语句
- 无条件循环 (Loop)
- For 循环
- while 循环
- 循环中的其它控制结构
- 分支语句
- if
- case
- 数组
- 数组定义
- 声明数组变量
- 访问数组元素
- 数组的其他特性
- record
- record 定义
- 创建和使用 Record 变量
- Record 的其他特性
- Variant record
- Nested record
- 子程序
- 过程 procedure
- 函数
- 参数传递
- Tutorial 1
Ada 常用的库和方法
Ada.Text_IO:
这个库提供了用于标准输入输出的一系列函数,包括处理数字的函数。例如,Get 和 Put 可以用来读取和输出整数或浮点数。
Ada.Integer_Text_IO:
这个库提供了更具体的用于处理整数的输入输出函数。
Ada.Float_Text_IO:
这个库提供了更具体的用于处理浮点数的输入输出函数。
Ada.Numerics:
这个库包含了一系列用于数值计算的函数和过程,包括一些数学函数如平方根、指数、对数等。
Ada.Numerics.Float_Random:
这个库提供了一个伪随机数生成器,可以生成浮点数的随机数。
Ada.Characters.Handling
字符类型函数
字符分类函数:
Is_Control(Item : in Character) return Boolean:
判断给定字符是否是控制字符。控制字符的位置在范围0…31或127…159之内。
Is_Graphic(Item : in Character) return Boolean:
判断给定字符是否是图形字符。图形字符的位置在范围32…126或160…255之内。
Is_Letter(Item : in Character) return Boolean:
判断给定字符是否是字母。字母字符的位置在’A’…‘Z’或’a’…‘z’的范围内,或者在192…214、216…246或248…255的范围内。
Is_Lower(Item : in Character) return Boolean:
判断给定字符是否是小写字母。小写字母字符在’a’…‘z’的范围内,或者在223…246或248…255的范围内。
Is_Upper(Item : in Character) return Boolean:
判断给定字符是否是大写字母。大写字母字符在’A’…‘Z’的范围内,或者在192…214或216… 222的范围内。
Is_Basic(Item : in Character) return Boolean:
判断给定字符是否是基本字母。基本字母字符在’A’…‘Z’和’a’…‘z’的范围内,或者是以下几个特殊字符:‘Æ’, ‘æ’, ‘Ð’, ‘ð’, ‘Þ’, ‘þ’, 或 ‘ß’。
Is_Digit(Item : in Character) return Boolean:
判断给定字符是否是十进制数字。十进制数字字符在’0’…‘9’的范围内。
Is_Decimal_Digit(Item : in Character) return Boolean:
这个函数是Is_Digit的别名。
Is_Hexadecimal_Digit(Item : in Character) return Boolean:
判断给定字符是否是十六进制数字。十六进制数字字符在’0’…‘9’,‘A’ … ‘F’或’a’ … 'f’的范围内。
Is_Alphanumeric(Item : in Character) return Boolean:
判断给定字符是否是字母或数字。
Is_Special(Item : in Character) return Boolean:
判断给定字符是否是特殊图形字符。特殊图形字符是不是字母或数字的图形字符。
Is_space(Item: Character)
判断是否为空格
转换函数
To_Lower(Item : in Character) return Character
和 To_Lower(Item : in String) return String:
转换给定字符或字符串为小写。
To_Upper(Item : in Character) return Character
和 To_Upper(Item : in String) return String:
转换给定字符或字符串为大写。
To_Basic(Item : in Character) return Character
和 To_Basic(Item : in String) return String:
如果给定字符或字符串中的字符是带有重音符号的字母,则将其转换为无重音符号的字母;
Ada 基础语法概览
- 可以参考 https://www.w3cschool.cn/ada/ada-shujuleixing.html
数据类型和子类型
在 Ada 中,类型(Type)和子类型(Subtype)是其类型系统的核心概念。
类型(Type)
在 Ada 中,类型定义了一组值和这些值上的一些操作。有许多内置类型,如整数类型 Integer,实数类型 Float,字符类型 Character,布尔类型 Boolean 等。此外,你可以定义自己的类型。类型定义可以是标量的(比如数字或枚举值),也可以是复合的(比如数组或记录)。
例如,以下是一些类型定义的示例:
type My_Integer is range 1 .. 100; -- 一个自定义的整数类型,值域是 1 到 100
type Day is (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday); -- 一个枚举类型
type Matrix is array (1 .. 10, 1 .. 10) of Float; -- 一个二维数组类型
子类型(Subtype)
子类型是某个类型的一个子集,可以通过约束来定义。约束可以是范围约束,也可以是判别约束(对于某些高级类型)。 每个类型都有一个对应的匿名子类型,这个子类型没有任何额外的约束。
**子类型的一个重要用途是提供额外的约束,以防止程序中出现不应该有的值。**例如:
subtype Positive is Integer range 1 .. Integer'Last; -- 一个表示正整数的子类型
在这个例子中,Positive
是 Integer
的一个子类型,它的值必须在 1
到 Integer'Last
之间。如果你试图给 Positive
赋一个负值或零,编译器会产生一个错误。
注意,虽然子类型提供了额外的约束,但是它并不是一个新的类型。 也就是说,你可以直接使用原类型的操作,不需要进行显式的类型转换。
类型和子类型的区别
最重要的区别是,类型定义了新的数据,而子类型只是原类型的一部分。 当你定义一个新的类型时,Ada 会提供默认的操作(比如赋值和比较),但是你不能直接使用原类型的操作。 另一方面,子类型可以直接使用原类型的所有操作,但是它提供了额外的约束,以限制可接受的值。
常用类型转换方法
在 Ada 中,类型转换通常是通过 “显式类型转换” 或者通过 “类型相关的函数” 来实现的。值得注意的是,Ada 的类型系统是强类型的,这意味着编译器通常不会自动将一个类型的值转换为另一个类型的值,你必须显式地进行转换。
以下是一些常见的类型转换方法:
显示类型转换
类型名(表达式)
例如:
declare
A : Integer := 42;
B : Float;
begin
B := Float(A);
end;
类型相关函数
有些类型提供了函数来进行类型转换。例如,你可以使用 Integer'Value
函数将字符串转换为整数:
declare
S : String := "42";
I : Integer;
begin
I := Integer'Value(S);
end;
同样地,你可以使用 Float'Value
、Boolean'Value
等函数来将字符串转换为其他类型。
这只是类型转换的基本方法。更复杂的类型转换可能需要使用到其他的技术,例如使用子程序或者运算符重载。
循环语句
在 Ada 编程语言中,有几种类型的循环语句,下面我将以 Markdown 格式列举和解释每一种:
无条件循环 (Loop)
无条件循环将重复执行循环体,直到显式的退出条件被满足。这通常通过 exit 语句来实现。
loop
-- 循环体
exit when 条件;
end loop;
For 循环
for 循环用于遍历一定范围内的数值或者迭代器的所有元素。在 Ada 中,for 循环的循环变量是不可变的, 也就是说你不能在循环体中修改它的值。
-- 数值范围
for I in 1 .. 10 loop
-- 循环体
end loop;
-- 迭代器
for Element of Some_Array loop
-- 循环体
end loop;
while 循环
while 循环在满足特定条件时执行循环体。如果条件在一开始就不满足,那么循环体可能一次都不会执行。
while 条件 loop
-- 循环体
end loop;
循环中的其它控制结构
Ada 还提供了一些其它的循环控制结构:
exit:
用于退出当前循环。
exit when:
当满足特定条件时退出当前循环。
next:
跳过当前循环迭代,进入下一次迭代。
next when:
当满足特定条件时跳过当前循环迭代,进入下一次迭代。
return:
从包含当前循环的过程或函数中返回。
分支语句
if
if 条件 then
-- 条件为真时执行的语句
end if;
if 语句可以包含一个或者多个 elsif 部分,还可以包含一个 else 部分:
if 条件1 then
-- 条件1为真时执行的语句
elsif 条件2 then
-- 条件1为假,但是条件2为真时执行的语句
else
-- 所有条件都为假时执行的语句
end if;
case
case 表达式 is
when 值1 =>
-- 表达式的值为值1时执行的语句
when 值2 =>
-- 表达式的值为值2时执行的语句
when others =>
-- 表达式的值不是任何给定值时执行的语句
end case;
case
语句可以处理的值必须是离散的
,例如整数或者枚举类型。when others
部分是可选的,但是如果没有它,那么 case 语句必须能够处理表达式所有可能的值。
在case
语句中,when
部分的值不能重复,也就是说不能有两个 when 部分处理相同的值。
数组
在 Ada 中,数组是一种复合数据类型,用于存储相同类型的数据项。数组由元素
和索引
组成。元素是数组中的数据项,每个元素都有一个与之关联的索引,用于唯一标识这个元素。
数组定义
在 Ada 中,你可以通过以下语法定义一个数组类型:
type Array_Type is array (Index_Type range <>) of Element_Type;
- 更具体一点:例如,以下代码定义了一个整数数组,其索引类型是
Integer
:
-- 括号中的 Integer 的意思是索引类型, of Integer 代表数据类型是 integer
type Integer_Array is array (Integer range <>) of Integer;
还可以定义多维数组。例如,以下代码定义了一个二维的整数数组:
type Matrix is array (Integer range <>, Integer range <>) of Integer;
声明数组变量
一旦你定义了数组类型,你就可以声明数组变量了。例如:
declare
A : Integer_Array(1 .. 10);
M : Matrix(1 .. 10, 1 .. 10);
begin
-- 这里可以使用数组 A 和 M
end;
在这个例子中,A 是一个包含 10
个元素的一维数组,M 是一个包含 100
个元素的二维数组。
访问数组元素
可以通过索引来访问数组的元素。例如:
declare
A : Integer_Array(1 .. 10);
begin
A(1) := 42; -- 设置数组的第一个元素
Put(A(1)); -- 输出数组的第一个元素
end;
对于多维数组,你需要提供多个索引。例如:
declare
M : Matrix(1 .. 10, 1 .. 10);
begin
M(1, 1) := 42; -- 设置数组的第一个元素
Put(M(1, 1)); -- 输出数组的第一个元素
end;
数组的其他特性
Ada 还提供了一些其他的数组特性,例如 数组切片、数组的赋值、数组的比较等。以下是一些例子:
- 数组切片:可以使用数组切片来访问数组的一部分。例如:
A(1 .. 5)
是数组 A 的前五个元素。 - 数组的赋值:你可以一次性赋值给整个数组。例如:
A := B
将数组 B 的所有元素赋值给 A。注意,A 和 B 必须是相同的类型。 - 数组的比较:你可以使用
=
和/=
运算符来比较两个数组是否相等。例如:如果 A = B 则表示 A 和 B 的所有元素都相等。
record
record 定义
在 Ada 编程语言中,record
是一种复合数据类型,它允许将不同类型的数据元素组合成一个单一的数据结构。这类似于 C 或 C++ 中的结构体(struct),或者 Python 中的类(class)。
Record
类型是由一组字段定义的,每个字段都有一个名称和一个类型。Record
类型的定义语法如下:
type Record_Type is record
Field1 : Type1;
Field2 : Type2;
-- 更多的字段...
end record;
例如,以下代码定义了一个名为 Person
的 record 类型,它有三个字段:Name、Age
和 Is_Employed
:
type Person is record
Name : String;
Age : Integer;
Is_Employed : Boolean;
end record;
创建和使用 Record 变量
定义了 Record 类型后,你可以创建此类型的变量,并使用 .
运算符访问其字段。例如:
declare
P : Person;
begin
P.Name := "Alice";
P.Age := 30;
P.Is_Employed := True;
Put("Name: " & P.Name);
Put("Age: " & Integer'Image(P.Age));
Put("Is employed: " & Boolean'Image(P.Is_Employed));
end;
在这个例子中,我们创建了一个 Person
类型的变量 P
,并设置了它的各个字段的值。
Record 的其他特性
Record 还有一些其他的特性,例如 variant record
和 nested record
。
Variant record
Variant record 允许 record 中的一部分字段在不同的情况下具有不同的类型和数量。这类似于 C 或 C++ 中的联合体(union)。
Nested record
Nested record:一个 record 可以包含另一个 record,这被称为 nested record。这允许你构建更复杂的数据结构。
子程序
过程 procedure
过程是一个子程序,它被用来执行一些操作,但不返回值。 过程的定义语法如下:
procedure Procedure_Name (Parameter_List) is
begin
-- 过程体
end Procedure_Name;
其中,Procedure_Name
是过程的名称,Parameter_List
是过程的参数列表,-- 过程体
是过程的主体,其中包含了要执行的代码。
例如,以下代码定义了一个名为 Print_Hello 的过程,该过程打印一条消息:
procedure Print_Hello is
begin
Put_Line("Hello, world!");
end Print_Hello;
函数
函数是一个子程序,它执行一些操作,并返回一个值。 函数的定义语法如下:
function Function_Name (Parameter_List) return Return_Type is
begin
-- 函数体
return Result;
end Function_Name;
其中,Function_Name
是函数的名称,Parameter_List
是函数的参数列表,Return_Type
是函数返回值的类型,-- 函数体 是函数的主体,其中包含了要执行的代码,Result
是要返回的结果。
例如,以下代码定义了一个名为 Add
的函数,该函数接受两个整数参数,并返回它们的和:
function Add (X, Y : Integer) return Integer is
begin
return X + Y;
end Add;
参数传递
参数传递
Ada 支持三种类型的参数传递:in、out
和 in out
。
-
In
:这是默认的参数类型,表示参数只能在子程序中被读取,不能被修改。 -
Out
:这种类型的参数在子程序调用之前不需要初始化,它们在子程序中被赋值,并在子程序结束后返回给调用者。 -
In Out
:这种类型的参数既可以在子程序中被读取,也可以被修改。在子程序结束后,修改后的值被返回给调用者。
以上就是 Ada 中子程序的一些基本知识。Ada 的子程序还有许多其他特性和细节没有在这里介绍,例如默认参数、子程序重载、子程序指针等等。
Tutorial 1
题目:Overview The WordCount program accepts a string from the standard input and delivers a count of words in the input string.
The following is a list of requirements:
- The program should accept a string characters on the standard input and count the number of words in the
input.- The input is terminated by a ‘#’ character.
- Words are separated by white spaces. To simplify the task, assume that white spaces are the space character (i.e. ignore tabs and new line characters).
- For the purposes of this workshop, a character that is not a whitespace character.
- The output is a single integer displaying the word count.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Characters.Handling; use Ada.Characters.Handling;
procedure Word_Count is
Input : Character;
Word_Cnt: Integer := 0;
In_Word : Boolean := False;
begin
loop
Get(Item => Input);
exit when Input = '#';
if Is_Space(Input) then
if In_Word then
Word_Cnt := Word_Cnt + 1;
end if;
In_Word := False;
else
In_Word := True;
end if;
end loop;
if In_Word then
Word_Cnt := Word_Cnt + 1;
end if;
Put_Line("Word count: " & Integer'Image(Word_Cnt));
end Word_Count;
题目:Implement the word counting program specified in the Specification section, and run a few tests to convince yourself that the program works on most inputs.
Tip: it is of course a good idea to implement your program incrementally. Start by writing and running a small program that extends the program given.
Note the following in the skeleton provided. ProtectedStack is importaed by adding the following:
- 这个题目是老师给出的解答,包括的操作有:
- 定义了一个
ProtectedStack
package
(包括 adb 和 ads 文件) - 在
solution
中构建了主逻辑
- 定义了一个
- 这个题目中涉及了使用
generic
类型的数据,在使用的时候需要先进行实例化is new
- 其实
StringStack
这个我们实例化的package
和之前的ProtectedStack
功能一样,只不过是它的实例而已
-- protectedStack.adb
package body ProtectedStack is
protected body Stack is
entry Push(I: in Item)
when True is
begin
if Stk.Size < Max_Size then
Stk.Size := Stk.Size + 1;
Stk.Data(Stk.Size) := I;
else
raise Stack_Overflow;
end if;
end Push;
entry Pop(I: out Item)
when True is
begin
if Stk.Size > 0 then
I := Stk.Data(Stk.Size);
Stk.Size := Stk.Size - 1;
else
raise Stack_Underflow;
end if;
end Pop;
entry Top(I: out Item)
when True is
begin
if Stk.Size > 0 then
I := Stk.Data(Stk.Size);
else
raise Stack_Underflow;
end if;
end Top;
entry Empty(EmptyStack: out Boolean)
when True is
begin
EmptyStack := (Stk.Size = 0);
end Empty;
entry Full(FullStack: out Boolean)
when True is
begin
FullStack := (Stk.Size = Max_Size);
end Full;
entry Clean
when True is
begin
Stk.Size := 0;
end Clean;
end Stack;
end ProtectedStack;
--protectedstack.ads
generic
Max_Size: Positive; -- The maximum size of the stack.
type Item is private; -- The type of items in the stack. The type must
-- be definite, and the private means that this
-- package may not examine its internals.
package ProtectedStack is
type StackType is private;
-- Exceptions.
Stack_Underflow, Stack_Overflow: exception;
-- The public interface to the stack consists of the following
-- operations. The stack is "protected" which means that the tasks
-- have muitually exclusive access to the stack operations. They
-- are declared just like the "entry" points in a task type
-- declaration and are implemented using "entry" keywords as
-- well. Entry calls are the main means of communication between
-- concurrent tasks in Ada so this is effectively an Abstract Data
-- Type that protects the Stack by enforcing mutually exlcusive
-- access.
protected type Stack is
entry Push(I: in Item);
entry Pop(I: out Item);
entry Top(I: out Item);
entry Empty(EmptyStack: out Boolean);
entry Full(FullStack: out Boolean);
entry Clean;
private
Stk : StackType;
end Stack;
private
type StackData is array(1.. Max_Size) of Item;
type StackType is record
Size: Integer range 0 .. Max_Size := 0;
Data: StackData;
end record;
end ProtectedStack;
-- solution
with Ada.Text_IO;
use Ada.Text_IO;
with Ada.Integer_Text_IO;
use Ada.Integer_Text_IO;
with ProtectedStack;
with Ada.Strings.Unbounded;
with Ada.Characters.Latin_1;
-- The WordCount program counts the number of characters and words in
-- a string received from standard input. A word is any alphanum
-- character separated by a space or tab
procedure WordCount is
package ASU renames Ada.Strings.Unbounded;
use ASU;
package StringStack is new ProtectedStack(100, ASU.Unbounded_String);
Ch : Character; -- the current character
Word : ASU.Unbounded_String; -- the current word
-- The number of characters and words
NumChars : Integer := 0;
NumWords : Integer := 0;
-- a stack for putting words into
St : StringStack.Stack;
-- for testing is the stack is empty
IsEmpty : Boolean;
begin
Get(Ch);
Word := ASU.To_Unbounded_String("");
while (Ch /= '#') loop
NumChars := NumChars + 1;
-- if a space or tab, we encounter a new word
if Ch = ' ' or Ch = Ada.Characters.Latin_1.HT then
-- consume remaining spaces and tabs
while Ch = ' ' or Ch = Ada.Characters.Latin_1.HT loop
Get(Ch);
end loop;
NumWords := NumWords + 1;
St.Push(Word);
Word := ASU.To_Unbounded_String("");
else
Word := Word & Ch;
Get(Ch);
end if;
end loop;
-- push the terminating word
NumWords := NumWords + 1;
St.Push(Word);
Put(NumWords); New_Line;
Put(NumChars); New_Line;
-- print the words on the stack
St.Empty(IsEmpty);
while not IsEmpty loop
St.Pop(Word);
Put(ASU.To_String(Word) & " ");
St.Empty(IsEmpty);
end loop;
end WordCount;
- 注意事项: