Delphi
Pascal
embarcadero
objectpascal

<5> 列挙型と部分範囲型 (標準 Pascal 範囲内での Delphi 入門)


5. 列挙型と部分範囲型

クラシック Pascal では列挙型は スカラ型という型として定義されていました。

さらに標準 Pascal の単純型スカラ型という分類として紹介されており、順序型実数型が分離されていなかったため、実数型の説明がちょっと苦しいものになっていました。

image.png

一般的にスカラ型というのは大小比較が可能な型の事です。その意味では文字も文字列もスカラ値を持つ型であるので、スカラ型という型を作ってしまうと混乱する事が予想されます。そして実際に混乱したのでしょう、標準 Pascal からはスカラ型の説明が消えています 1

標準 Pascal では型としてのスカラ型は列挙型に相当し、分類としてのスカラ型は順序型に相当します。

image.png


5.1. 列挙型 (Enumerated Types)

列挙型 は値を表す識別子を並べた値の集合で、ユーザー定義の型です。

列挙型 = 

 "(" 識別子 {"," 識別子} ")".

最初の識別子は 0、その次は 1 というように順序値を持ちます。例えば曜日を表す TDayOfTheWeek という列挙型は次のように定義できます。


EnumTest.pas

program EnumTest(output); 

type
TDayOfTheWeek = (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
var
d: TDayOfTheWeek;
begin
for d:=Mon to Sun do
Writeln(Ord(d));
end.

Ord() 関数は順序型の順序値を返すのでしたね。

type

TDayOfTheWeek = (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
TPokemon = (Sun, Moon);

続けて TPokemon のような列挙型を定義する事はできません。Sun という識別子が重複しているからです。

型を再利用しないのであれば、列挙型の定義と同時に変数を宣言する事が可能です。


EnumTest2.pas

program EnumTest2(output);

var
DayOfTheWeek: (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
begin
DayOfTheWeek := Wed;
Writeln(Ord(DayOfTheWeek));
end.

使い所が難しいのですが、Delphi では列挙型の値を列挙型とインデックス値で指定できます。


EnumTest3.pas

program EnumTest3(output); 

{$APPTYPE Console}
type
TDayOfTheWeek = (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
var
d: TDayOfTheWeek;
begin
d := TDayOfTheWeek(2);
Writeln(Ord(d));
end.

上の例では 2 と表示されます。当たり前といえば当たり前ですが。また、Delphi では明示的に値の順序値を設定する事もできます。

type

TDayOfTheWeek = (Mon = 1, Tue = 2, Wed = 3, Thu = 4, Fri = 5, Sat = 6, Sun = 7);

この場合の TDayOfTheWeek(2) の値も 2 になります。3 ではありません。

Boolean 型を列挙型で定義してみると次のようになります。

type

Boolean = (False, True);

確かに順序型スカラ型 (列挙型)で定義する事が可能なので、クラシック Pascal の説明が完全に間違っている訳ではありません。

しかしながら、クラシック Pascal では Real は実数の部分集合という扱いになっているのです。スカラ型 (列挙型)ではどうやっても実数型を定義できないと思うのですが...。

See also:


(5.1.1) 列挙型定数とグローバル列挙型変数の初期化

Delphi では次のような列挙型定数が使えます。

program EnumConstantsTest;

{$APPTYPE CONSOLE}

// グローバル列挙型定数
const
DayOfTheWeek1: (Mon, Tue, Wed, Thu, Fri, Sat, Sun) = Sun;

// グローバル列挙型変数
var
DayOfTheWeek2: (Mon2, Tue2, Wed2, Thu2, Fri2, Sat2, Sun2) = Sun2;

procedure Sub;
//var
// ローカル列挙型変数は初期化できない
// DayOfTheWeek2: (Mon3, Tue3, Wed3, Thu3, Fri3, Sat3, Sun3) = Sun3;
begin
...
end; { Sub }
begin
...
end.

但し、列挙型の型付き定数にあまり意味があるとは思えません。


5.2. 部分範囲型 (Subrange Types)

部分範囲型 は他の順序型の値の部分集合で、ユーザー定義の型です。

部分範囲型 = 

 定数 ".." 定数.

値の範囲を .. で指定します。

type 

TDayOfTheWeek = (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
TBool = False..True; (* 論理型の部分範囲型 *)
TDice = 1..6; (* 整数型の部分範囲型 *)
TAlphabet = 'A'..'Z'; (* 文字型の部分範囲型 *)
TWorkDays = Mon..Fri; (* 列挙型の部分範囲型 *)

例えば次のようなコードは Delphi だと範囲チェックが行われるためコンパイル時にエラーになります。


SubrangeTest.pas

program SubrangeTest(output); 

type
TDice = 1..6;
var
D: TDice;
begin
D := 7; (* Error! *)
Writeln(D);
end.

型を再利用しないのであれば、部分範囲型の定義と同時に変数を宣言する事が可能です。


SubrangeTest2.pas

program SubrangeTest2(output);

var
Dice: 1..6;
begin
Dice := 6;
Writeln(Dice);
end.

See also:


(5.2.1) 部分範囲型定数とグローバル部分範囲型変数の初期化

Delphi では次のような部分範囲型定数が使えます。

program SubrangeConstantsTest;

{$APPTYPE CONSOLE}

// グローバル部分範囲型定数
const
Dice1: 1..6 = 6;

// グローバル部分範囲型変数
var
Dice2: 1..6 = 6;

procedure Sub;
//var
// ローカル部分範囲型変数は初期化できない
// Dice3: 1..6 = 6;
begin
...
end; { Sub }
begin
...
end.

但し、部分範囲型の型付き定数にあまり意味があるとは思えません。


索引

[ ← 4. 動作の概念 ] [ ↑ 目次へ ] [ → 6. 構造型の概要と配列型 ]





  1. Turbo Pascal も ver 3.0 まではスカラ型で説明されていました。