5. 列挙型と部分範囲型
クラシック Pascal では列挙型は スカラ型という型として定義されていました。
さらに標準 Pascal の単純型がスカラ型という分類として紹介されており、順序型と実数型が分離されていなかったため、実数型の説明がちょっと苦しいものになっていました。次の文は『J&W』第2版からの引用です。
実数はスカラ型に含めているが、必ずしも他のスカラ型と同じ意味で使用できるとは限らない。特に, 関数 pred と succ は実数型の引数をとることはできないし, 配列要素の指定や for 文の制御, あるいは集合の基底の型 (第8章参照) の定義に実数型の値を使用する事はできない。
一般的にスカラ型というのは大小比較が可能な型の事です。その意味では文字も文字列もスカラ値を持つ型であるので、スカラ型という型を作ってしまうと混乱する事が予想されます。そして実際に混乱したのでしょう、標準 Pascal からはスカラ型の説明が消えています。
標準 Pascal では型としてのスカラ型 (狭義のスカラ型) は列挙型に相当し、分類としてのスカラ型 (広義のスカラ型) は順序型に相当します。
5.1. 列挙型 (Enumerated Types)
列挙型 は値を表す識別子を並べた値の集合で、プログラマ定義の型 1 です。
列挙型 =
"(" 識別子 {"," 識別子} ")".
最初の識別子は 0、その次は 1 というように順序値を持ちます。例えば曜日を表す TDayOfTheWeek という列挙型は次のように定義できます。
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.
type
TDayOfTheWeek = (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
TPokemon = (Sun, Moon);
続けて TPokemon のような列挙型を定義する事はできません。Sun
という識別子が重複しているからです。
型を再利用しないのであれば、列挙型の定義と同時に変数を宣言する事が可能です。
program EnumTest2(output);
var
DayOfTheWeek: (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
begin
DayOfTheWeek := Wed;
Writeln(Ord(DayOfTheWeek));
end.
使い所が難しいのですが、Delphi では列挙型の値を列挙型とインデックス値で指定できます。
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) スコープのある列挙型 (Scoped Enums)
Delphi 2009 以降で追加された {$SCOPEDENUMS ON}
指令を使うと、スコープのある列挙型を作る事ができ、異なる列挙型で同じ列挙型要素を定義可能となります。
type
TDayOfTheWeek = (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
{$SCOPEDENUMS ON}
TPokemon = (Sun, Moon); // Sun が使える
スコープのある列挙型では、列挙型要素を型名によって修飾する必要があります。
var
a: TDayOfTheWeek;
b: TPokemon;
begin
a := Sun;
b := TPokemon.Sun;
b := Moon; // エラー
end.
See also:
(5.1.2) 列挙型定数とグローバル列挙型変数の初期化
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)
部分範囲型 は他の順序型の値の部分集合で、プログラマ定義の型 1 です。
部分範囲型 =
定数 ".." 定数.
値の範囲を ..
で指定します。
type
TDayOfTheWeek = (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
TBool = False..True; (* 論理型の部分範囲型 *)
TDice = 1..6; (* 整数型の部分範囲型 *)
TAlphabet = 'A'..'Z'; (* 文字型の部分範囲型 *)
TWorkDays = Mon..Fri; (* 列挙型の部分範囲型 *)
標準 Pascal では符号なし整数型は用意されていませんが、次のようにして Byte 型を定義できます。
type
Byte = 0..255;
例えば次のようなコードは Delphi だと範囲チェックが行われるためコンパイル時にエラーになります。
program SubrangeTest(output);
type
TDice = 1..6;
var
D: TDice;
begin
D := 7; (* Error! *)
Writeln(D);
end.
型を再利用しないのであれば、部分範囲型の定義と同時に変数を宣言する事が可能です。
program SubrangeTest2(output);
var
Dice: 1..6;
begin
Dice := 6;
Writeln(Dice);
end.
Delphi では部分範囲型の定数に式が使えます。
var
TRange: 0..1024 - 1;
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. 構造型の概要と配列型 ]