5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2019-03-11

5. 列挙型と部分範囲型

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

さらに標準 Pascal の単純型スカラ型という分類として紹介されており、順序型実数型が分離されていなかったため、実数型の説明がちょっと苦しいものになっていました。次の文は『J&W』第2版からの引用です。

実数はスカラ型に含めているが、必ずしも他のスカラ型と同じ意味で使用できるとは限らない。特に, 関数 pred と succ は実数型の引数をとることはできないし, 配列要素の指定や for 文の制御, あるいは集合の基底の型 (第8章参照) の定義に実数型の値を使用する事はできない。

image.png

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

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

image.png

5.1. 列挙型 (Enumerated Types)

列挙型 は値を表す識別子を並べた値の集合で、プログラマ定義の型 1 です。

列挙型 = 
 "(" 識別子 {"," 識別子} ")".

最初の識別子は 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) スコープのある列挙型 (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 だと範囲チェックが行われるためコンパイル時にエラーになります。

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.

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. 構造型の概要と配列型 ] :sushi:

  1. プログラマ定義の型 は ISO/IEC 7185 の構文定義では new-type、JIS X 3008 では 書下(くだ)し型 と記述されています。『J&W』の構文定義には記述がありません。 2

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?