# <5> 列挙型と部分範囲型 (標準 Pascal 範囲内での Delphi 入門) --- tags: Delphi プログラミング Pascal embarcadero objectpascal created_at: 2019-03-07 updated_at: 2024-02-05 --- # 5. 列挙型と部分範囲型 **クラシック Pascal** では列挙型は **スカラ型という型**として定義されていました。 さらに標準 Pascal の[単純型](./5c04bb2b42947e29d94b.md)が**スカラ型という分類**として紹介されており、[順序型](./5c04bb2b42947e29d94b.md#21-%E9%A0%86%E5%BA%8F%E3%83%87%E3%83%BC%E3%82%BF%E5%9E%8B-%E9%A0%86%E5%BA%8F%E5%9E%8B)と[実数型](./5c04bb2b42947e29d94b.md#25-real-%E5%9E%8B)が分離されていなかったため、[実数型](./5c04bb2b42947e29d94b.md#25-real-%E5%9E%8B)の説明がちょっと苦しいものになっていました。次の文は『J&W』第2版からの引用です。 > 実数はスカラ型に含めているが、必ずしも他のスカラ型と同じ意味で使用できるとは限らない。特に, 関数 pred と succ は実数型の引数をとることはできないし, 配列要素の指定や for 文の制御, あるいは集合の基底の型 (第8章参照) の定義に実数型の値を使用する事はできない。 ![image.png](./images/a344da80-f856-53a3-fe6d-4a6596afb76c.png) 一般的にスカラ型というのは**大小比較が可能な型**の事です。その意味では文字も文字列も**スカラ値を持つ型**であるので、**スカラ型という型**を作ってしまうと混乱する事が予想されます。そして実際に混乱したのでしょう、標準 Pascal からはスカラ型の説明が消えています。 標準 Pascal では型としてのスカラ型 (狭義のスカラ型) は列挙型に相当し、分類としてのスカラ型 (広義のスカラ型) は[順序型](./5c04bb2b42947e29d94b.md#21-%E9%A0%86%E5%BA%8F%E3%83%87%E3%83%BC%E3%82%BF%E5%9E%8B-%E9%A0%86%E5%BA%8F%E5%9E%8B)に相当します。 ![image.png](./images/3f456b4e-93e7-94a7-5fb2-bd8ad33f6b18.png) ## 5.1. 列挙型 (Enumerated Types) **列挙型** は値を表す識別子を並べた値の集合で、プログラマ定義の型 [^1] です。 ``` 列挙型 =  "(" 識別子 {"," 識別子} ")". ``` 最初の識別子は 0、その次は 1 というように順序値を持ちます。例えば曜日を表す TDayOfTheWeek という列挙型は次のように定義できます。 ```pascal: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()](http://docwiki.embarcadero.com/Libraries/ja/System.Odd) 関数は[順序型](./5c04bb2b42947e29d94b.md#21-%E9%A0%86%E5%BA%8F%E3%83%87%E3%83%BC%E3%82%BF%E5%9E%8B-%E9%A0%86%E5%BA%8F%E5%9E%8B)の順序値を返すのでしたね。 ```pascal type TDayOfTheWeek = (Mon, Tue, Wed, Thu, Fri, Sat, Sun); TPokemon = (Sun, Moon); ``` 続けて TPokemon のような列挙型を定義する事はできません。`Sun` という識別子が重複しているからです。 型を再利用しないのであれば、列挙型の定義と同時に変数を宣言する事が可能です。 ```pascal:EnumTest2.pas program EnumTest2(output); var DayOfTheWeek: (Mon, Tue, Wed, Thu, Fri, Sat, Sun); begin DayOfTheWeek := Wed; Writeln(Ord(DayOfTheWeek)); end. ``` 使い所が難しいのですが、Delphi では列挙型の値を列挙型とインデックス値で指定できます。 ```pascal: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 では明示的に値の順序値を設定する事もできます。 ```pascal type TDayOfTheWeek = (Mon = 1, Tue = 2, Wed = 3, Thu = 4, Fri = 5, Sat = 6, Sun = 7); ``` この場合の TDayOfTheWeek(2) の値も 2 になります。3 ではありません。 Boolean 型を列挙型で定義してみると次のようになります。 ```pascal type Boolean = (False, True); ``` 確かに[順序型](./5c04bb2b42947e29d94b.md#21-%E9%A0%86%E5%BA%8F%E3%83%87%E3%83%BC%E3%82%BF%E5%9E%8B-%E9%A0%86%E5%BA%8F%E5%9E%8B)は**スカラ型 (列挙型)**で定義する事が可能なので、**クラシック Pascal** の説明が完全に間違っている訳ではありません。 しかしながら、**クラシック Pascal** では Real は実数の部分集合という扱いになっているのです。**スカラ型 (列挙型)**ではどうやっても[実数型](./5c04bb2b42947e29d94b.md#25-real-%E5%9E%8B)を定義できないと思うのですが...。 **See also:** - [列挙型 (DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/%E5%8D%98%E7%B4%94%E5%9E%8B%EF%BC%88Delphi%EF%BC%89#.E5.88.97.E6.8C.99.E5.9E.8B) ### (5.1.1) スコープのある列挙型 (Scoped Enums) Delphi 2009 以降で追加された `{$SCOPEDENUMS ON}` 指令を使うと、**スコープのある列挙型**を作る事ができ、異なる列挙型で同じ列挙型要素を定義可能となります。 ```pascal type TDayOfTheWeek = (Mon, Tue, Wed, Thu, Fri, Sat, Sun); {$SCOPEDENUMS ON} TPokemon = (Sun, Moon); // Sun が使える ``` スコープのある列挙型では、列挙型要素を型名によって修飾する必要があります。 ```pascal var a: TDayOfTheWeek; b: TPokemon; begin a := Sun; b := TPokemon.Sun; b := Moon; // エラー end. ``` **See also:** - [スコープのある列挙型 (DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/%E5%8D%98%E7%B4%94%E5%9E%8B%EF%BC%88Delphi%EF%BC%89#.E3.82.B9.E3.82.B3.E3.83.BC.E3.83.97.E3.81.AE.E3.81.82.E3.82.8B.E5.88.97.E6.8C.99.E5.9E.8B) - [$SCOPEDENUMS 指令 (DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%97%E3%81%AE%E3%81%82%E3%82%8B%E5%88%97%E6%8C%99%E5%9E%8B%EF%BC%88Delphi%EF%BC%89) ### (5.1.2) 列挙型定数とグローバル列挙型変数の初期化 Delphi では次のような列挙型定数が使えます。 ```pascal 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) **部分範囲型** は他の[順序型](./5c04bb2b42947e29d94b.md#21-%E9%A0%86%E5%BA%8F%E3%83%87%E3%83%BC%E3%82%BF%E5%9E%8B-%E9%A0%86%E5%BA%8F%E5%9E%8B)の値の部分集合で、プログラマ定義の型 [^1] です。 ``` 部分範囲型 =  定数 ".." 定数. ``` 値の範囲を `..` で指定します。 ```pascal type TDayOfTheWeek = (Mon, Tue, Wed, Thu, Fri, Sat, Sun); TBool = False..True; (* 論理型の部分範囲型 *) TDice = 1..6; (* 整数型の部分範囲型 *) TAlphabet = 'A'..'Z'; (* 文字型の部分範囲型 *) TWorkDays = Mon..Fri; (* 列挙型の部分範囲型 *) ``` 標準 Pascal では符号なし整数型は用意されていませんが、次のようにして Byte 型を定義できます。 ```pascal type Byte = 0..255; ``` 例えば次のようなコードは Delphi だと範囲チェックが行われるためコンパイル時にエラーになります。 ```pascal:SubrangeTest.pas program SubrangeTest(output); type TDice = 1..6; var D: TDice; begin D := 7; (* Error! *) Writeln(D); end. ``` 型を再利用しないのであれば、部分範囲型の定義と同時に変数を宣言する事が可能です。 ```pascal:SubrangeTest2.pas program SubrangeTest2(output); var Dice: 1..6; begin Dice := 6; Writeln(Dice); end. ``` Delphi では部分範囲型の定数に式が使えます。 ```pascal var TRange: 0..1024 - 1; ``` **See also:** - [部分範囲型 (DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/%E5%8D%98%E7%B4%94%E5%9E%8B%EF%BC%88Delphi%EF%BC%89#.E9.83.A8.E5.88.86.E7.AF.84.E5.9B.B2.E5.9E.8B) ### (5.2.1) 部分範囲型定数とグローバル部分範囲型変数の初期化 Delphi では次のような部分範囲型定数が使えます。 ```pascal 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. 動作の概念](./05af01a81f7c3bb5eeb4.md) ] [ [↑ 目次へ](./e2796788c65c2cda1de5.md) ] [ [→ 6. 構造型の概要と配列型](./eedda6d38b6d0887d4ac.md) ] [:sushi:](./e581e8f5c0c32d5a58e2.md) [^1]: `プログラマ定義の型` は ISO/IEC 7185 の構文定義では **new-type**、JIS X 3008 では **書下(くだ)し型** と記述されています。『J&W』の構文定義には記述がありません。