コンテンツにスキップ

Ada

出典: フリー教科書『ウィキブックス(Wikibooks)』
Wikipedia
Wikipedia
ウィキペディアAdaの記事があります。

このAdaチュートリアルは、Adaプログラミング言語に興味を持っている人、または既にAdaを知っているがより深く理解したいと思っている人向けに作成されています。 Adaは、高信頼性が求められるシステム開発や組込みシステム開発に適した言語であり、現在でも多くの産業分野で使われています。

このチュートリアルでは、Ada言語の基本的な概念から始め、プログラミングの基礎を解説しています。初心者でも理解しやすいように、具体的な例を交えながら、丁寧に説明していきます。

このチュートリアルを通じて、Adaをより深く理解し、高信頼性の高いソフトウェア開発に役立てていただければ幸いです。

Adaの概要

[編集]

[Adaがどのようなプログラミング言語であるか、開発の背景や利点について説明します。]

Adaは、1980年代にアメリカ合衆国の国防総省が開発した高信頼性ソフトウェアを開発するためのプログラミング言語です。その後、民間分野でも利用されるようになり、現在でも航空宇宙、鉄道、医療機器など、高い信頼性が求められる分野で広く使われています。

Adaの特徴としては、以下のような点が挙げられます。

  • 強い型付け:変数や関数などのデータ型が厳密に定義され、コンパイル時に型エラーを検出できるため、バグの発生を防止できます。
  • 相互排除機能:タスク間の相互排除機能が強力で、デッドロックやライブロックなどの問題が少なく、安全な並行処理を実現できます。
  • メモリ管理:Adaにはガベージコレクション機能があるため、メモリリークや二重解放のような問題が起こりにくく、安全なメモリ管理が可能です。
  • 大規模なプログラムの開発に適した機能:Adaには、パッケージ、抽象型、ジェネリック、タスクなど、大規模なプログラムの開発に必要な機能が豊富にあります。
  • ドキュメント生成:Adaには、コードから自動的に文書を生成する機能があり、ソフトウェア開発における文書作成の手間を省くことができます。

Adaの利点としては、高信頼性、安全性、保守性、品質の高さなどが挙げられます。ただし、Adaの学習曲線がやや高く、他の一般的な言語と比べると、使いこなすのに時間がかかることがあります。

環境の設定

[編集]

[Adaの開発に必要なソフトウェアやツールをインストールし、環境を構築する方法について説明します。]

Adaの開発環境を構築するためには、GNATというコンパイラをインストールする必要があります。GNATは、Ada言語のフリーでオープンソースな実装であり、GNU Compiler Collection (GCC) に含まれています。

Debian linux へのインストール

[編集]
  1. ターミナルを開きます。
  2. sudo apt updateコマンドを実行して、パッケージリストを更新します。
  3. sudo apt-get install gnatコマンドを実行して、GNATパッケージをインストールします。
  4. インストールが完了したら、gnat --versionコマンドを実行して、GNATのバージョンを確認します。

MSYS2/Windows へのインストール

[編集]

MSYS2にGNATをインストールする手順は以下の通りです。

  1. MSYS2をインストールします。公式サイトからダウンロードして、指示に従ってインストールしてください。
  2. MSYS2を開きます。開いたコンソールで以下のコマンドを実行して、パッケージリストを更新します。
    pacman -Syu
    
  3. GNATをインストールするために、以下のコマンドを実行して必要なパッケージをインストールします。 このコマンドは、GNATのパッケージに含まれているmingw-w64-x86_64-gcc-adaパッケージをインストールします。
    pacman -S mingw-w64-x86_64-gcc-ada
    
  4. インストールが完了したら、gnat --versionコマンドを実行して、GNATのバージョンを確認します。

macOS へのインストール

[編集]

macOSにGNATをインストールする手順は以下の通りです。

  1. Homebrewをインストールします。ターミナルを開いて、以下のコマンドを実行します。
    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
    
  2. GNATをインストールするために、以下のコマンドを実行します。 このコマンドは、GNATのパッケージをHomebrewからインストールします。
    brew install gnat
    
  3. インストールが完了したら、gnat --versionコマンドを実行して、GNATのバージョンを確認します。

FreeBSD へのインストール

[編集]

FreeBSDにGNATをインストールする手順は以下の通りです。

  1. pkgを更新します。ターミナルを開いて、以下のコマンドを実行します。
    % sudo pkg update
    
  2. GNATをインストールするために、以下のコマンドを実行します。 このコマンドは、GNATのパッケージに含まれているgcc-adaパッケージをインストールします。
    % sudo pkg install gcc-ada
    
  3. インストールが完了したら、gnat --versionコマンドを実行して、GNATのバージョンを確認します。
GNATについて
GNATは、GNU Ada Compilerとして知られており、GNUプロジェクトの一部であるため、一般的にはGNU Adaと呼ばれています。GNATの名称は、GNU Ada Translatorの略称であり、最初のリリースが1987年に行われた際には、Adaのコンパイラとしては初めてGNUプロジェクトに含まれていたことから、GNATと名付けられました。その後、GNATはGNUプロジェクトの一部として、オープンソースのAdaコンパイラとして発展してきました。 ただし、GNATの商用版であるGNAT Proは、GNU Adaではなく、AdaCore社が独自に開発した製品として販売されています。GNAT Proは、GNAT GPLに比べて商用利用に適した機能やサポートが提供されています。また、GNAT Proの利益の一部は、GNUプロジェクトに寄付されています。

トップレベルのプログラム構造

[編集]

Adaプログラムのトップレベルの構造は、以下のようになります。

with パッケージ名;
use パッケージ名;

プログラム名 is

    -- 定数、型、変数、サブプログラム、タスクなどの宣言

begin

    -- プログラムの実行部分

end プログラム名;


with文は、使用するパッケージを指定するために使用されます。use文は、パッケージの使用方法を指定します。

プログラム名は、プログラムの名前を指定します。プログラム名の下に、定数、型、変数、サブプログラム、タスクなどを宣言することができます。

beginキーワードの後には、プログラムの実行部分が続きます。

ソースコードのファイル名のルールは、以下のようになります。

  • ファイル名は、プログラム名と同じにする必要があります。
  • Adaのソースファイルは、.adb(本体)と.ads(仕様)の2つの拡張子を持つことができます。
  • .adbファイルと.adsファイルのペアは、同じ名前を持つ必要があります。
  • ただし、プログラムが一つの.adbファイルで完結している場合、.adsファイルは不要です。

例えば、以下のようなファイル構成になります。

my_program.adb  -- プログラムの本体
my_program.ads  -- プログラムの仕様

コメント

[編集]

Adaのコメントは、ダブルハイフン(--)ではじまり、どの行にも表示することができます。コメントはコンパイラによって無視され、コードを記録するために使用されます。

以下は、Adaのコメントの例です:

-- これはコメントです。

コメントは、コードの一部の目的を説明したり、追加情報を提供したり、現在使用されていないコードのセクションをマークするために使用することができます。

コメントは、コードをより読みやすく、理解しやすくするための貴重なツールです。また、コードの保守性を向上させ、デバッグを容易にする効果もあります。


データ型と変数

[編集]

Adaには、さまざまなデータ型があります。この章では、Adaで使用できる主要なデータ型と変数の宣言方法、データの代入や演算について説明します。

データ型

[編集]

Adaには、以下のような主要なデータ型があります。

  • 整数型(Integer)
  • 実数型(Float)
  • 文字型(Character)
  • 真偽値型(Boolean)
  • 列挙型(Enumeration)
  • 配列型(Array)
  • レコード型(Record)
  • 構造体型(Access)

これらのデータ型を組み合わせて、より複雑なデータ型を定義することもできます。

整数型(Integer)

[編集]

整数型(Integer)は、Adaで最も一般的なデータ型の一つであり、符号付き整数値を表します。Integerの範囲は実装によって異なりますが、通常は-2^31から2^31-1までの値を表すことができます。以下は、Integer型を使用したコード例です。

integer_example.adb
with Ada.Text_IO;         use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure Integer_Example is
   age : Integer := 25;
begin
   Put("I am ");
   Put(age, 2);
   Put(" years old.");
end Integer_Example;

実数型(Float)

[編集]

実数型(Float)は、浮動小数点数値を表すために使用されます。Adaには、いくつかの異なる精度のFloat型があります。例えば、Float型は通常、単精度の浮動小数点数を表し、Long_Float型は倍精度の浮動小数点数を表します。以下は、Float型を使用したコード例です。

float_example.adb
with Ada.Text_IO;       use Ada.Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;

procedure Float_Example is
   pi : Float := 3.14159;
begin
   Put("The value of Pi is ");
   Put(pi, 1, 5);
end Float_Example;

文字型(Character)

[編集]

文字型(Character)は、単一の文字を表します。Adaの文字型は、Unicode文字セットをサポートしています。以下は、Character型を使用したコード例です。

character_example.adb
with Ada.Text_IO; use Ada.Text_IO;

procedure Character_Example is
   letter : Character := 'A';
begin
   Put("The first letter of the alphabet is ");
   Put(letter);
end Character_Example;

真偽値型(Boolean)

[編集]

真偽値型(Boolean)は、真または偽の論理値を表します。以下は、Boolean型を使用したコード例です。

boolean_example.adb
with Ada.Text_IO; use Ada.Text_IO;

procedure Boolean_Example is
   is_raining : Boolean := True;
begin
   if is_raining then
      Put("Bring an umbrella.");
   else
      Put("Leave your umbrella at home.");
   end if;
end Boolean_Example;

列挙型(Enumeration)

[編集]

列挙型(Enumeration)は、指定された値の集合を表します。例えば、以下のコード例では、曜日を表すために、列挙型を使用しています。

enumeration_example.adb
with Ada.Text_IO; use Ada.Text_IO;

procedure Enumeration_Example is
   type Day is (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday);
   today : Day := Tuesday;
begin
   Put("Today is ");
   case today is
      when Monday =>
         Put("Monday");
      when Tuesday =>
         Put("Tuesday");
      when Wednesday =>
         Put("Wednesday");
      when Thursday =>
         Put("Thursday");
      when Friday =>
         Put("Friday");
      when Saturday =>
         Put("Saturday");
      when Sunday =>
         Put("Sunday");
   end case;
end Enumeration_Example;

配列型(Array)

[編集]

配列型(Array)は、同じデータ型の値の集合を表します。配列は、固定サイズまたは可変サイズの場合があります。以下は、Integer型の配列を使用したコード例です。

erray_example.adb
with Ada.Text_IO; use Ada.Text_IO;

procedure Array_Example is
   -- 配列を宣言する
   type My_Array is array(1..5) of Integer;
   A : My_Array := (1, 2, 3, 4, 5);
begin
   -- 配列の要素にアクセスする
   Put_Line("A(1) = " & Integer'Image(A(1)));
   Put_Line("A(2) = " & Integer'Image(A(2)));

   -- 配列の要素を変更する
   A(1) := 10;
   A(2) := 20;

   -- 変更された要素を出力する
   Put_Line("A(1) = " & Integer'Image(A(1)));
   Put_Line("A(2) = " & Integer'Image(A(2)));

   -- 配列の全要素にアクセスする
   for I in A'range loop
      Put_Line("A(" & Integer'Image(I) & ") = " & Integer'Image(A(I)));
   end loop;
end Array_Example;

レコード型(Record)

[編集]

レコード型(Record)は、異なるデータ型の値の集合を表します。レコードには、名前付きフィールドがあります。以下は、Personという名前のレコード型を使用したコード例です。

record_example.adb
with Ada.Text_IO; use Ada.Text_IO;

procedure Record_Example is
   type Person is record
      Name : String(1..20);
      Age : Integer;
      Is_Married : Boolean;
   end record;

   p : Person := ("John", 30, False);
begin
   Put("Name: ");
   Put(p.Name);
   New_Line;
   Put("Age: ");
   Put(p.Age, 2);
   New_Line;
   Put("Married: ");
   if p.Is_Married then
      Put("Yes");
   else
      Put("No");
   end if;
end Record_Example;

構造体型(Access)

[編集]

構造体型(Access)は、動的に確保されたデータ構造を表します。構造体には、名前付きフィールドがあります。以下は、Personという名前のAccess型を使用したコード例です。

with Ada.Text_IO; use Ada.Text_IO;

procedure Access_Example is
   type Person is record
      Name : String(1..20);
      Age : Integer;
      Is_Married : Boolean;
   end record;

   type Person_Ptr is access Person;

   p : Person_Ptr :== new Person'(Name ==> "John", Age ==> 30, Is_Married ==> False);
begin
   Put("Name: ");
   Put(p.Name);
   New_Line;
   Put("Age: ");
   Put(p.Age, 2);
   New_Line;
   Put("Married: ");
   if p.Is_Married then
      Put("Yes");
   else
      Put("No");
   end if;
end Access_Example;

変数の宣言

[編集]

変数を宣言するには、以下のような構文を使用します。

variable_name : data_type := initial_value;

変数名は、英数字とアンダースコアから構成されます。データ型は、前述のデータ型のいずれかを指定します。初期値はオプションで、変数の宣言時に代入されます。

例えば、整数型の変数xを宣言し、初期値を10に設定する場合は、以下のようになります。

x : Integer := 10;

データの代入

[編集]

変数にデータを代入するには、以下のような構文を使用します。

variable_name := new_value;

変数名は、代入先の変数を指定します。新しい値は、代入演算子:=の後に続けて指定します。

例えば、上記の例で宣言した整数型の変数xに、新しい値20を代入する場合は、以下のようになります。

x := 20;

式と演算子

[編集]

[編集]

Ada言語では、式とは変数、定数、リテラル値、関数、および演算子を含むコードの断片です。式は、式自体が値を返すように評価され、変数の値を取得したり、関数を呼び出したりすることができます。

例えば、以下のコードは、式を使用して変数xとyの値を加算し、結果を変数zに格納します。

with Ada.Text_IO; use Ada.Text_IO;

procedure Main is
   x, y, z : Integer;
begin
   x := 10;
   y := 20;
   
   z := x + y;
   
   Put_Line(z'Image);
end Main;

演算子

[編集]

演算子とは、算術、比較、論理、ビット演算など、プログラムで使用される操作を表す特殊な記号です。演算子は、一般に一つまたは複数のオペランドに適用されます。

Adaには、次のような演算子の種類があります。

  • 算術演算子: +、-、*、/、**(べき乗)
  • 比較演算子: =、/=、<、<=、>、>=
  • 論理演算子: and、or、not
  • ビット演算子: and、or、xor、not、shl(左シフト)、shr(右シフト)

Adaには、C言語のような複雑な演算子がないため、明確でわかりやすいコードが書けます。

Adaに特有の演算子の一つは、範囲演算子(..)です。これは、範囲を表すために使用されます。たとえば、1から10までの整数の範囲を表すには、1..10と書きます。

また、Adaには、剰余演算子(mod)があります。これは、2つの整数の剰余を計算します。

以下は、Adaで算術演算子を使用した例です。

with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
   X, Y, Z : Integer;
begin
   X := 10;
   Y := 3;
   
   Z := X + Y;
   Put_Line("X + Y = " & Integer'Image(Z));
   
   Z := X - Y;
   Put_Line("X - Y = " & Integer'Image(Z));
   
   Z := X * Y;
   Put_Line("X * Y = " & Integer'Image(Z));
   
   Z := X / Y;
   Put_Line("X / Y = " & Float'Image(Z));
   
   Z := X ** Y;
   Put_Line("X ** Y = " & Integer'Image(Z));
end Main;
出力
X + Y = 13
X - Y = 7
X * Y = 30
X / Y = 3.333333
X ** Y = 1000

以下に、Adaの演算子を分類ごとに一覧表にまとめました。

算術演算子
演算子 説明
+ 加算
- 減算
* 乗算
/ 除算
mod 除余
** べき乗
比較演算子
演算子 説明
= 等しい
/= 等しくない
< 未満
<= 以下
> より大きい
>= 以上
論理演算子
演算子 説明
and 論理積
or 論理和
not 否定
ビット演算子
演算子 説明
and ビットごとの論理積
or ビットごとの論理和
xor ビットごとの排他的論理和
not ビットごとの否定
shl 左シフト
shr 右シフト
その他の演算子
演算子 説明
& 文字列の連結
:= 代入
[] 配列の要素の参照
() 手続きや関数の引数の指定、グループ化
. レコードのフィールドの参照
.. 範囲を表す


以上の表に示された演算子を適切に使用することで、Adaプログラムをより効率的かつわかりやすく記述することができます。

制御構造

[編集]

制御構造(Control Structures)は、プログラムの流れを制御するための構造であり、プログラムをより複雑な動作を行えるようにします。Adaでは、主に条件分岐(if文)とループ(for文)が利用されます。

条件分岐(if文)

[編集]

条件分岐は、条件によって処理を分岐させるための構造です。if文を用いて、条件が真である場合と偽である場合で、それぞれ異なる処理を行うことができます。

単純なif文
if 条件 then
    真の場合の処理;
end if;
上記の構文では、条件が真である場合に真の場合の処理が実行されます。偽である場合は、何も処理されません。
if-else文
if 条件 then
    真の場合の処理;
else
    偽の場合の処理;
end if;
上記の構文では、条件が真である場合に真の場合の処理が実行され、偽である場合には偽の場合の処理が実行されます。
if-elsif-else文
if 条件1 then
    条件1が真の場合の処理;
elsif 条件2 then
    条件2が真の場合の処理;
else
    条件1も条件2も偽の場合の処理;
end if;
上記の構文では、条件1が真の場合には条件1が真の場合の処理が実行され、条件2が真の場合には条件2が真の場合の処理が実行され、どちらも偽の場合には、else以下の偽の場合の処理が実行されます。

case文

[編集]

Adaにおけるcase文は、複数の条件分岐を行うために使用される制御構造の一つです。case文は、値によって異なるブロックを実行することができます。

case文は以下のような構文を持ちます。

case expression is
   when choice1 =>
      -- choice1の処理
   when choice2 =>
      -- choice2の処理
   ...
   when choiceN =>
      -- choiceNの処理
   when others =>
      -- どの条件にも一致しない場合の処理
end case;

expressionには条件分岐の対象となる式を指定します。choiceには条件分岐の候補となる値を指定します。whenキーワードの後に続くchoiceに該当する場合、そのブロック内の処理が実行されます。最後に、when othersを使用して、どの条件にも該当しない場合の処理を指定することができます。

反復

[編集]

Adaにおけるループ(繰り返し)は、同じ処理を繰り返し実行するための制御構造です。Adaには、以下の3つのループがあります。

  • loopループ
  • whileループ
  • forループ

loopループ

[編集]

loopループは、条件式がfalseになるまで、繰り返し処理を実行します。以下は、loopループの構文です。

loop
   -- 実行する処理
end loop;

以下は、loopループの例です。

with Ada.Text_IO; use Ada.Text_IO;

procedure Example is
   I : Integer := 1;
begin
   loop
      Put_Line("I is " & Integer'Image(I));
      I := I + 1;
      exit when I = 5;
   end loop;
end Example;

この例では、Iが5になるまで、Put_LineでIの値を表示する処理を繰り返しています。exit when文を使用して、Iが5になった時点でループを終了するようにしています。

whileループ

[編集]

whileループは、条件式がtrueの間、繰り返し処理を実行します。以下は、whileループの構文です。

while condition loop
   -- 実行する処理
end loop;

以下は、whileループの例です。

with Ada.Text_IO; use Ada.Text_IO;

procedure Example is
   I : Integer := 1;
begin
   while I <= 5 loop
      Put_Line("I is " & Integer'Image(I));
      I := I + 1;
   end loop;
end Example;

この例では、Iが5以下である限り、Put_LineでIの値を表示する処理を繰り返しています。

forループ

[編集]

forループは、初期値から終了値まで、指定されたステップで繰り返し処理を実行します。以下は、forループの構文です。

for variable in range loop
   -- 実行する処理
end loop;

以下は、forループの例です。

with Ada.Text_IO; use Ada.Text_IO;

procedure Example is
begin
   for I in 1..5 loop
      Put_Line("I is " & Integer'Image(I));
   end loop;
end Example;

この例では、1から5までの数値を、Iに順番に代入してPut_LineでIの値を表示する処理を繰り返しています。

まとめ

[編集]

以下は、条件分岐やループなどの制御構造をすべて使用した Ada のプログラム例です。

control_structures.adb
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Control_Structures is
   I : Integer;
   Num : Integer;
begin
   -- if-then-else statement
   Put("Enter a number: ");
   Get(Num);
   if Num > 0 then
      Put_Line("The number is positive.");
   elsif Num = 0 then
      Put_Line("The number is zero.");
   else
      Put_Line("The number is negative.");
   end if;

   -- case statement
   Put("Enter a number between 1 and 3: ");
   Get(I);
   case I is
      when 1 =>
         Put_Line("You entered 1.");
      when 2 =>
         Put_Line("You entered 2.");
      when 3 =>
         Put_Line("You entered 3.");
      when others =>
         Put_Line("You entered something else.");
   end case;

   -- for loop
   Put_Line("Printing numbers from 1 to 10:");
   for I in 1..10 loop
      Put(I, 2);
      Put(" ");
   end loop;
   New_Line;

   -- while loop
   I := 1;
   Put_Line("Printing odd numbers from 1 to 10:");
   while I <= 10 loop
      if I mod 2 = 1 then
         Put(I, 2);
         Put(" ");
      end if;
      I := I + 1;
   end loop;
   New_Line;

   -- exit statement
   Put_Line("Printing numbers from 1 to 10 until 6 is reached:");
   for I in 1..10 loop
      if I = 6 then
         exit;
      end if;
      Put(I, 2);
      Put(" ");
   end loop;
   New_Line;

end Control_Structures;

このプログラムでは、ユーザーからの入力を受け取り、if-then-else 文を使用して入力された数値の符号を判定し、case 文を使用して入力された数値の値によって分岐します。 さらに、for ループと while ループを使用して、数値を繰り返し出力し、exit 文を使用してループから抜け出すことも示しています。

サブプログラム

[編集]

[手続きや関数の宣言方法、引数の渡し方、戻り値の取り扱いなどを説明します。]

サブプログラムは、メインプログラムの中で実行される独立したコードのブロックであり、手続きと関数の2つの形式があります。手続きは、何かを実行するためのサブプログラムであり、戻り値を返さないのに対して、関数は値を返します。

手続きの宣言

[編集]
procedure Procedure_Name (parameter1, parameter2, ...) is
    -- Declaration statements
begin
    -- Statements
end Procedure_Name;

関数の宣言

[編集]
function Function_Name (parameter1, parameter2, ...) return Return_Type is
    -- Declaration statements
begin
    -- Statements
    return return_value;
end Function_Name;

引数の渡し方

[編集]

Adaでは、引数を参照渡し、値渡し、および不変渡しの3つの方法で渡すことができます。参照渡しは、引数がサブプログラム内で変更された場合、呼び出し元の変数にも反映されます。値渡しは、引数をサブプログラムにコピーするため、呼び出し元の変数には影響を与えません。不変渡しは、呼び出し元の変数を保護するため、サブプログラム内で引数を変更することはできません。

procedure Example (A : in Integer; B : out Integer; C : in out Integer) is
begin
    B := A + 1;  -- 変数Bには値が代入される
    C := C + 1;  -- 変数Cには値が代入され、呼び出し元の変数にも反映される
end Example;

戻り値の取り扱い

[編集]

関数は常に値を返します。そのため、関数の戻り値を変数に代入することができます。

function Add (A : Integer; B : Integer) return Integer is
begin
    return A + B;
end Add;

-- メインプログラム
X := Add(2, 3);  -- Xには5が代入される

オブジェクト指向プログラミング

[編集]

[Adaでのオブジェクト指向プログラミングの基本概念や、クラスの定義方法、継承、ポリモーフィズムなどを説明します。]

オブジェクト指向プログラミング(OOP)は、プログラムをオブジェクトと呼ばれる単位に分割し、それらの相互作用によってプログラムを構成するプログラミングの方法論です。AdaもOOPに対応しており、以下に基本概念やクラスの定義方法などを説明します。

クラスの定義方法

[編集]

クラスは、データ型の定義に似た方法で定義されます。例えば、以下はクラス Point を定義する例です。

-- Pointクラスを定義
package Point_Class is
   type Point is tagged record
      X, Y : Integer;
   end record;

   procedure Set_Point(P : in out Point; X, Y : Integer);
   function Get_X(P : Point) return Integer;
   function Get_Y(P : Point) return Integer;
end Point_Class;

クラスの宣言は、package(パッケージ)文で行われます。type文によって、Point という名前のクラスが定義され、tagged(タグ付き)に設定されます。タグ付きのクラスは、派生型(クラス)を定義する際に必要です。また、record文によって、クラスのメンバーが定義されます。

次に、クラスのメンバーにアクセスするための手続きや関数を定義します。この例では、プロシージャ Set_Point でクラスのインスタンス P のメンバー X と Y に値を設定し、関数 Get_X, Get_Y でそれぞれの値を取得することができます。

継承

[編集]

Adaは、継承をサポートしています。派生型(サブクラス)は、基底型(スーパークラス)を拡張することができます。例えば、以下はクラス Circle を定義する例で、Point クラスを継承しています。

-- Circleクラスを定義
package Circle_Class is
   type Circle is new Point with record
      Radius : Integer;
   end record;

   procedure Set_Circle(C : in out Circle; X, Y, R : Integer);
   function Get_Radius(C : Circle) return Integer;
end Circle_Class;

type文の new 構文で、派生型 Circle が Point クラスを継承することが指定されています。また、with record で、Circle クラスに独自のフィールド Radius を追加しています。継承元のクラス(スーパークラス)のフィールドは、引き続き Circle クラスでも使用することができます。

Adaに多重継承はありますか?
いいえ、Adaには多重継承はありません。Adaプログラミング言語は、単一継承の原則を採用しています。これは、あるクラス(Adaでは"型"と呼ばれる)が他のクラスからのみ継承できることを意味します。これにより、クラスの階層構造が簡潔になり、クラス間の複雑な関係が減少します。

Adaの選択には、単一継承が理解しやすく、シンプルであるという設計思想が影響しています。多重継承は柔軟性を提供しますが、クラスの階層構造が複雑になる可能性があり、コードの理解やメンテナンスが難しくなることがあるため、Adaは意図的にこれを避けています。

代わりに、Adaはインターフェースの実装、複数のインターフェースのサポート、およびミックスイン(mix-in)のような機能を提供し、これにより柔軟なオブジェクト指向プログラミングが可能です。

例外処理

[編集]

[例外が発生した場合の処理方法について説明します。]

Adaには例外処理機構が備わっており、例外が発生した場合に対処することができます。例外は、実行時に発生するエラーであり、予期しない状況が発生した場合に発生することがあります。例えば、ゼロ除算、配列の境界外アクセス、不正な入力、メモリ不足などが挙げられます。

例外処理の構文は以下のようになります。

begin
   -- 例外が発生する可能性のある処理
exception
   when Exception1 =>
      -- Exception1が発生した場合の処理
   when Exception2 =>
      -- Exception2が発生した場合の処理
   ...
   when others =>
      -- 上記以外の例外が発生した場合の処理
end;

ここで、Exception1やException2は、例外の種類を示す例外名であり、具体的な例外名はアプリケーションによって異なります。

例外処理は、try-catch構文のような構造であるため、プログラムが例外をキャッチした場合、該当する例外処理ブロックが実行されます。othersキーワードは、上記の例外以外のすべての例外をキャッチするために使用されます。

以下は、例外処理の例です。

with Ada.Text_IO;
use Ada.Text_IO;

procedure Exception_Handling is
   A : Integer := 10;
   B : Integer := 0;
   Result : Integer;

   -- ゼロ除算を検出する例外処理関数
   function Divide(A, B : Integer) return Integer is
   begin
      if B = 0 then
         raise Program_Error;
      else
         return A / B;
      end if;
   end Divide;

begin
   -- Divide関数でゼロ除算が発生した場合、
   -- Program_Error例外が発生するため、例外処理が実行される
   begin
      Result := Divide(A, B);
      Put_Line("Result: " & Result'Image);
   exception
      when Program_Error =>
         Put_Line("Error: Division by zero.");
   end;

   Put_Line("End of program.");
end Exception_Handling;

この例では、Divide関数でBが0である場合、Program_Error例外が発生します。Program_Error例外は、Adaの標準例外であり、実行時エラーが発生した場合に自動的にスローされます。Divide関数を実行するために、try-catch構文を使用し、Program_Error例外をキャッチし、エラーメッセージを表示します。

タスク

[編集]

[タスクの定義方法やスケジューリング、相互排除などについて説明します。]

タスクとは、並行処理を実現するために使用される概念です。タスクは、自己完結型で独立したスレッドのようなもので、自分自身が実行可能であると同時に、他のタスクからのメッセージを受信して処理を行うこともできます。タスクのスケジューリングは、Adaランタイムシステムによって自動的に制御されます。

タスクの宣言方法は、以下のようになります。

task Task_Name is
   entry Entry_Name (Parameter_List);
end Task_Name;

ここで、Task_Name はタスクの名前、Entry_Name はタスクから呼び出されるエントリーの名前、Parameter_List はエントリーに渡される引数のリストです。

また、タスク間の相互排除には、以下のような手段があります。

  • セマフォ(Semaphore)
  • ミューテックス(Mutex)
  • リモート・リード、ローカル・マルチプル・ライト(Remote_Read, Local_Multiple_Write)

パッケージ

[編集]

[Adaでのパッケージの作成方法や使用方法、名前空間の機能について説明します。]

パッケージは、Adaの名前空間を実現するための機能で、関連するサブプログラムや変数をまとめることができます。パッケージには、公開されるインターフェース部と、実装部があります。インターフェース部には、他のモジュールからアクセス可能なサブプログラムや変数を定義し、実装部には、これらのサブプログラムや変数の実装を記述します。

パッケージの宣言方法は、以下のようになります。

package Package_Name is
   -- インターフェース部
end Package_Name;

package body Package_Name is
   -- 実装部
end Package_Name;

ファイル入出力

[編集]

[ファイルを開いて読み書きする方法について説明します。]

Adaにおけるファイル操作は、ストリーム(Stream)と呼ばれる機能を使用して行います。ファイルを開くには、以下のようにします。

with Ada.Text_IO; use Ada.Text_IO;

procedure Main is
   F : File_Type;
begin
   Create (F, Out_File, "output.txt");
   -- 出力先のファイルを "output.txt" に設定
end Main;

ファイルからの入力や、ファイルへの出力は、Get_Line、Put_Line、Read、Write などのサブプログラムを使用して行います。

デバッグとテスト

[編集]

[Adaのデバッグやテストの方法について説明します。]

Adaには、コンパイル時の静的検査やランタイム時の動的検査など、様々なレベルの検査が備わっています。

まず、コンパイル時の検査についてです。Adaは、型安全性が非常に高い言語です。静的型検査機能により、変数や定数の型が一致しない場合、または未初期化の変数を使用した場合など、コンパイル時にエラーが発生します。これにより、多くの実行時エラーを未然に防止できます。

ランタイム時の検査として、例外処理があります。プログラム中で発生する予期せぬエラーに対応するため、例外処理を使用することができます。例外が発生した場合は、例外の種類に応じた処理を行うことができます。

また、Adaにはコードカバレッジツールも含まれています。コードカバレッジツールは、テスト中に実行されたコードの割合を測定するために使用されます。これにより、どのコードがテストされていないかを特定し、テストの改善点を見つけることができます。

附録

[編集]

チートシート

[編集]
with Ada.Text_IO; use Ada.Text_IO;

procedure Subprogram_Cheat_Sheet is
  -- 関数の定義
  function Add(a, b : Integer) return Integer is
  begin
    return a + b;
  end Add;

  -- 手続きの定義
  procedure Greet(name : in String) is
  begin
    Put("Hello, ");
    Put(name);
    Put_Line("!");
  end Greet;

  -- 引数の渡し方
  procedure Pass_By_Value(x : Integer) is
  begin
    x := 0; -- 値を変更しても呼び出し元の変数に影響はない
  end Pass_By_Value;

  procedure Pass_By_Reference(x : in out Integer) is
  begin
    x := 0; -- 値を変更すると呼び出し元の変数にも影響がある
  end Pass_By_Reference;

  -- 戻り値の取り扱い
  function Divide(a, b : Integer) return Float is
    Result : Float;
  begin
    if b = 0 then
      raise Constraint_Error with "Division by zero";
    else
      Result := Float(a) / Float(b);
    end if;
    return Result;
  end Divide;

  -- 例外処理
  procedure Exception_Handling is
  begin
    Put("Enter an integer: ");
    declare
      Num : Integer := Integer'Value(Get_Line);
    begin
      if Num < 0 then
        raise Constraint_Error with "The number must be non-negative";
      else
        Put("The square root of ");
        Put(Num, Width => 0);
        Put(" is ");
        Put(Float'Sqrt(Float(Num)), 2);
        New_Line;
      end if;
    exception
      when Constraint_Error =>
        Put_Line("Error: " & Exception_Message);
    end;
  end Exception_Handling;

begin
  -- テスト
  Put_Line("Add(1, 2) = " & Integer'Image(Add(1, 2)));
  Greet("Alice");

  -- 引数の渡し方
  declare
    x : Integer := 1;
  begin
    Pass_By_Value(x);
    Put_Line("x = " & Integer'Image(x)); -- 1
    Pass_By_Reference(x);
    Put_Line("x = " & Integer'Image(x)); -- 0
  end;

  -- 戻り値の取り扱い
  Put_Line("Divide(4, 2) = " & Float'Image(Divide(4, 2)));
  Put_Line("Divide(4, 0) = " & Float'Image(Divide(4, 0))); -- Constraint_Error

  -- 例外処理
  Exception_Handling;
end Subprogram_Cheat
with Ada.Text_IO; use Ada.Text_IO;

procedure OOP_Cheat_Sheet is

   -- クラスの定義
   type MyClass is tagged record
      x: Integer;
   end record;

   -- クラスの継承
   type MySubclass is new MyClass with record
      y: Integer;
   end record;

   -- ポリモーフィズム
   type MyClass_Access is access all MyClass'Class;
   procedure MyProc (Obj : MyClass_Access'Class);

   -- クラスメソッド
   function Get_X (Obj : MyClass'Class) return Integer is
   begin
      return Obj.x;
   end Get_X;

   -- クラスの操作
   procedure Set_X (Obj : in out MyClass'Class; Val : Integer) is
   begin
      Obj.x := Val;
   end Set_X;

   -- クラスの初期化
   overriding procedure Initialize (Obj : in out MyClass'Class) is
   begin
      Obj.x := 0;
   end Initialize;

   -- クラスの終了処理
   overriding procedure Finalize (Obj : in out MyClass'Class) is
   begin
      null;
   end Finalize;

   -- クラスの利用例
   Obj : MyClass'Class := new MyClass;
begin
   Set_X (Obj, 42);
   Put_Line (Integer'Image (Get_X (Obj)));
end OOP_Cheat_Sheet;

用語集

[編集]
  1. パッケージ(package):関連するサブプログラムや型、定数などをひとまとめにした、独立したコンパイル単位。パッケージ仕様とパッケージ本体に分かれる。
  2. タイプ(type):データの種類を定義する。整数型、浮動小数点型、文字型などがある。型の定義には、レコード型や配列型なども使用できる。
  3. モジュール(module):Adaプログラムの構成単位。複数のパッケージやサブプログラムから構成され、単一のコンパイル単位となる。
  4. サブプログラム(subprogram):繰り返し使われる処理をまとめた、関数や手続きなどの部品。関数は値を返し、手続きは値を返さない。
  5. 引数(parameter):サブプログラムに与える入力値。サブプログラム内でのみ使用できる変数であり、呼び出し元の変数に影響を与えない。
  6. 例外(exception):プログラムの実行中に発生するエラーを処理するためのメカニズム。例外が発生した場合は、例外処理部分に制御が移る。
  7. 継承(inheritance):オブジェクト指向プログラミングにおける、新しいクラスの定義時に既存のクラスを基にして新たな機能を追加する仕組み。
  8. ポリモーフィズム(polymorphism):オブジェクト指向プログラミングにおける、異なるクラスのオブジェクトを同じインタフェースで扱うことができる性質。
  9. タスク(task):並列処理を実現するための機能。Adaでは、タスクの生成、同期、通信などを行うことができる。
  10. ファイル(file):外部記憶装置に格納されたデータを扱うためのデータ型。ファイルをオープンすることで、ファイルからの入力やファイルへの出力が可能になる。
  11. 名前空間(namespace):名前の衝突を回避するための仕組み。パッケージの名前空間は、パッケージ内で定義されます。