Ada Tutorial(1):Ada基础——wordcount程序

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 CharacterTo_Lower(Item : in String) return String: 转换给定字符或字符串为小写。
To_Upper(Item : in Character) return CharacterTo_Upper(Item : in String) return String: 转换给定字符或字符串为大写。
To_Basic(Item : in Character) return CharacterTo_Basic(Item : in String) return String: 如果给定字符或字符串中的字符是带有重音符号的字母,则将其转换为无重音符号的字母;

Ada 基础语法概览

数据类型和子类型

在 Ada 中,类型(Type)和子类型(Subtype)是其类型系统的核心概念。

类型(Type)

在 Ada 中,类型定义了一组值和这些值上的一些操作。有许多内置类型,如整数类型 Integer,实数类型 Float,字符类型 Character,布尔类型 Boolean 等。此外,你可以定义自己的类型。类型定义可以是标量的(比如数字或枚举值),也可以是复合的(比如数组或记录)。

例如,以下是一些类型定义的示例:

type My_Integer is range 1 .. 100;  -- 一个自定义的整数类型,值域是 1100
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;  -- 一个表示正整数的子类型

在这个例子中,PositiveInteger 的一个子类型,它的值必须在 1Integer'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'ValueBoolean'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
   when1 =>
      -- 表达式的值为值1时执行的语句
   when2 =>
      -- 表达式的值为值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、AgeIs_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 recordnested 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、outin 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:

  1. The program should accept a string characters on the standard input and count the number of words in the
    input.
  2. The input is terminated by a ‘#’ character.
  3. 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).
  4. For the purposes of this workshop, a character that is not a whitespace character.
  5. 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;

  • 注意事项:
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_42902997/article/details/131160770