列挙型

**列挙型**（れっきょがた、あるいは）とは、コンピュータプログラミングにおいて、取り得る値の集合をあらかじめ定義して一つの型として扱うデータ型である。

状態・種別・選択肢など、値が限定される情報の表現に用いられる。各値は識別子（列挙子）によって表され、値の誤用を防ぎやすくするとともに、プログラムの可読性を高める役割を持つ。

## 特徴
列挙型は一般に、カードのスートのように番号順を持たないカテゴリ変数として使われるが、実際のコンパイル時あるいは実行時には、列挙型は整数で実装されることが多い。各々の識別子は通例異なる整数値を持つが、複数の識別子に対して意図的に同じ整数値を割り当てる（つまり別名を定義する）ことも可能である。

また列挙型は、整数を使用する場合と比較して、明示的にマジックナンバーを使用するよりもプログラムソースの可読性を改善するのに役立つ。言語によっては、列挙型の整数表現はプログラマに見えないようになっていることもあり、これによりプログラマが列挙値に対して算術演算を行うような乱用を防いでいる。列挙型の定数は同じ列挙型の変数にしか代入できない（整数型の変数に代入するには明示的な型変換などが必要）という仕様になっている言語もある。

プログラミング言語によっては、真偽値の論理型は、あらかじめ宣言された二値の列挙型とされている。

C言語では構造体および共用体のアナロジーとして、列挙体（）とも呼ばれる。

## Pascalおよび類似言語
### Pascal
Pascalでは、列挙型は括弧で括られた値のリストによって暗黙的に宣言できる。

```pascal
var
    suit: (clubs, diamonds, hearts, spades);
```

複数の変数に対して使うことができるように、列挙型の宣言は、`type`を使った型のシノニム（別名）宣言に現れることが多い。

```pascal
type
    cardsuit = (clubs, diamonds, hearts, spades);
    card = record
             suit: cardsuit;
             value: 1 .. 13;
           end;
  var
    hand: array [ 1 .. 13 ] of card;
    trump: cardsuit;
```


列挙値の順序は書いた順番になる。列挙型は順序型 **' であり、<code>Pred</code>、<code>Succ</code>関数は、列挙の前または次の値を与え、 <code>Ord</code>は列挙値を整数表現に変換する。しかし、標準Pascalでは数値型から列挙型への変換はできない。拡張Pascalは拡張された<code>Succ</code>関数経由でこの機能性を提供する。他のPascal派生言語の一部では、型キャストで変換可能なものもある。

=== Ada ===
Adaでは、Pascalとよく似た定義となるが"="の代わりに"is"を用いる。
<syntaxhighlight lang="ada">
type Cardsuit is (Clubs, Diamonds, Hearts, Spades);
</syntaxhighlight>
属性Pred, Succ, ValおよびPosに加えて、Adaにはさらに属性ImageおよびValueがあり、文字列変換を行うことができる（Image: 列挙値から文字列への変換、Value: 文字列から列挙値への変換）。

Cスタイルの言語と同様に、Adaでは列挙値として数値に何を用いるかを指定することができる。
<syntaxhighlight lang="ada">
for Cardsuit use (Clubs => 1, Diamonds => 2, Hearts => 4, Spades => 8);
</syntaxhighlight>
Cスタイルの言語と異なり、Adaでは列挙値に割り当てるビット数を指定することができる。
<syntaxhighlight lang="ada">
for Cardsuit'Size use 4;  -- 4 ビット
</syntaxhighlight>
さらに列挙値を配列の添字として用いることもできる。
<syntaxhighlight lang="ada">
Shuffle : constant array(Cardsuit) of Cardsuit :=
   (Clubs => Cardsuit'Succ(Clubs), -- Clubsの"次の値"を指定: Diamondsになる
    Diamonds => Hearts,      -- 列挙リテラル: 直接Heartsを指定
    Hearts => Cardsuit'Last, -- 列挙型の"最後の値"を指定: Spadesになる
    Spades => Cardsuit'First -- 列挙型の"最初の値"を指定: Clubsになる
   );
</syntaxhighlight>

[[Modula-3]]と同様に、AdaではBooleanやCharacterは列挙型の一種に過ぎない（パッケージ"Standard"で既定義）。Modula-3と異なり、Adaでは自前の文字型も定義することができる。
<syntaxhighlight lang="ada">
type Cards is ("8", "9", "T", "J", "Q", "K", "A");
</syntaxhighlight>

== Cおよび類似の言語 ==
Cを受け継ぐ言語はほとんどが列挙型をサポートする。ただし[[Perl]]や[[JavaScript]]列挙型をもたない。

=== C言語 ===
[[C言語]]のオリジナルの[[C言語#K&R|K&R]]に列挙型は存在しなかったが、[[ANSI]]標準 (C89) で追加された。Cでは、列挙体は<code>enum</code>キーワードを使った明示的な定義によって生成できる。<code>enum</code>によって定数群を宣言することができるが、<code>const</code>と違って記憶域の割り当てを伴わない。これは[[構造体]]と[[共用体]]宣言を連想させる。
<syntaxhighlight lang="c">
enum cardsuit {
    CLUBS,
    DIAMONDS,
    HEARTS,
    SPADES
    /* C99 以降では最終要素の後ろにコンマを付けてもよい */
};

struct card {
    enum cardsuit suit;
    short int number;
} hand[13];

enum cardsuit trump;
</syntaxhighlight>
Cは列挙値の"小さな整数"表現を直接プログラマに公開する。Cの列挙体は整数型の一種である。整数値と列挙値は自由に変換可能であり、列挙値でも全ての数値演算が可能となっている。結果として、列挙体に定義されていない値すら取りえることもある。事実、言語仕様によると、上記のコードは**int**型の定数として<code>CLUBS</code>、<code>DIAMONDS</code>、<code>HEARTS</code>、<code>SPADES</code>を定義しているが、これらがその型の変数に保存されるときは、（暗黙裡に）<code>enum cardsuit</code>に変換されるだけである。

[[typedef]]によりエイリアスを定義することも可能である。
<syntaxhighlight lang="c">
typedef enum cardsuit cardsuit_t;
int a = DIAMONDS;
cardsuit_t b = DIAMONDS;
cardsuit_t c = 1;
</syntaxhighlight>

Cでは、プログラマが明示的に列挙定数の値を指定することも可能である。例えば、
<syntaxhighlight lang="c">
enum cardsuit {
    CLUBS    = 1,
    DIAMONDS = 2,
    HEARTS   = 4,
    SPADES   = 8
};
</syntaxhighlight>
は、スートの数学的な集合をビット演算によって<code>enum cardsuit</code>として表現することを可能にする型を定義する目的で使える。

値を指定しなかった場合、最初の列挙定数の値は<code>0</code>となる。後続の列挙定数の値は、直前の列挙定数の値を<code>1</code>だけインクリメントしたものとなる。

型名を持たない無名の列挙体を定義することも可能である。
<syntaxhighlight lang="c">
enum {
    CLUBS,
    DIAMONDS,
    HEARTS,
    SPADES
};
</syntaxhighlight>

標準Cでは空の構造体が定義できないのと同様に、空の列挙体も定義できない。
<syntaxhighlight lang="c">
enum SomeEnum {}; /* コンパイルエラー */
</syntaxhighlight>

=== C++ ===
[[C++]]はCから直接引き継いだ列挙体（列挙型）を持っている。しかしCとは異なり列挙型は整数型ではない。列挙定数はint型ではなく基の列挙型となる（ただしint型へ[[汎整数拡張|昇格]]できる。整数型から列挙型への変換には明示的なキャストが必要である）。このため、整数型と列挙型との間で[[多重定義]]できる。また、列挙型は利用者定義型の一種であるということから、列挙型に対しての[[演算子多重定義]]も可能となっている。

C言語とは異なり、C++では曖昧さがない限りenumキーワードで修飾せずとも列挙型をそのまま型名として使用できる。これは構造体や共用体の使用時にstructやunionキーワードによる修飾が不要であるのと同じである。

<syntaxhighlight lang="c++">
enum cardsuit {
    CLUBS,
    DIAMONDS,
    HEARTS,
    SPADES
    // C++11 以降では最終要素の後ろにコンマを付けてもよい。
};

int a = DIAMONDS; // 暗黙的に代入可能。
cardsuit b = DIAMONDS;
cardsuit c = 1; // コンパイルエラー。
</syntaxhighlight>

C++では空の構造体が定義できるのと同様に、空の列挙体も定義できる。
<syntaxhighlight lang="c++">
enum SomeEnum {}; // コンパイル可能。
</syntaxhighlight>

[[C++11]]では、スコープ付きの強く型付けされた列挙型（enum classもしくはenum struct）が新たに実装された。
<syntaxhighlight lang="c++">
enum class Cardsuit { Clubs, Diamonds, Spades, Hearts };
int a = Cardsuit::Hearts; // コンパイルエラー。
Cardsuit b = Cardsuit::Hearts;
</syntaxhighlight>

=== C# ===
[[C Sharp|C#]]の列挙型は<code>System.Enum</code>から暗黙的に派生する値型であり、Cのenumの意味する多くの"小さな整数"を保持する。いくつかの数値演算はenumでは定義されないが、enum値は明示的に整数値に型変換することができ、また整数値から元に戻すこともできる。またenum変数はenum宣言によって定義されなかった値を保存できる。例として、
<syntaxhighlight lang="csharp">
enum Cardsuit { Clubs, Diamonds, Spades, Hearts }
</syntaxhighlight>
が与えられたとき、式<code>Diamonds + 1</code>と<code>Hearts - Clubs</code>は直接許可される（ループで一連の値を走査することや、二つのenumの間にいくつのステップがあるか計算することはありうる）が、<code>Hearts * Spades</code>は理にかなっていないと考えられ、値が最初に整数に変換されるということだけが許可される。

また、<code>FlagsAttribute</code>を指定することで、ビット演算が許可される。
<syntaxhighlight lang="csharp">
/// <summary>光の三原色を1bitずつで表す</summary>
[Flags]
enum RgbColor : byte {
    Black = 0, Red = 1, Green = 2, Blue = 4,
    Cyan = Green | Blue,
    Magenta = Blue | Red,
    Yellow = Red | Green,
    White = Red | Green | Blue,
}
</syntaxhighlight>

=== Groovy ===
[[Groovy]]は[[動的型付け|動的型付き言語]]だが、[[Java]]由来の列挙型をサポートする。
<syntaxhighlight lang="groovy">
enum Cardsuit { Clubs, Diamonds, Spades, Hearts }
</syntaxhighlight>

=== Java ===
[[Java]]は[[Java Platform, Standard Edition|Java SE]] (J2SE) version 5.0から列挙型を導入した。
<syntaxhighlight lang="java">
// 列挙型の定数シンボルはすべて大文字とするのが慣習。
enum Cardsuit { CLUBS, DIAMONDS, SPADES, HEARTS }
...
Cardsuit trump = Cardsuit.CLUBS;
</syntaxhighlight>
Javaの型システムは、整数から分離された型として列挙を扱うが、（メソッドを使用してenum値の整数表現を取得できることを除き）enumと整数値との混合演算は許されていない。実際には、Javaのenum型は現に、数値型というよりもむしろ、コンパイラによって生成された特殊な[[クラス (コンピュータ)|クラス]]である。enum値はそのクラスのあらかじめ生成されたグローバルなインスタンスとして振る舞う。enum型はインスタンスメソッドとコンストラクタ（引数が各々のenum値を分割指定できる）を持つ。全てのenum型は暗黙のうちに[[抽象クラス]]を継承している。enum型を直接インスタンス化することは許可されない。各列挙型には、静的メソッド<code>values()</code>が暗黙的に定義され、列挙型に含まれるすべての定数の集合を配列として取得することができる。

=== Kotlin ===
<syntaxhighlight lang="kotlin">
// 列挙型の定数シンボルはすべて大文字とするのが慣習。
enum class Cardsuit { CLUBS, DIAMONDS, SPADES, HEARTS }
</syntaxhighlight>

=== TypeScript ===
<syntaxhighlight lang="typescript">
enum Cardsuit { Clubs, Diamonds, Spades, Hearts };
</syntaxhighlight>

== その他の手続き型言語 ==
=== Fortran ===
<syntaxhighlight lang="fortran">
enum, bind( C )
  enumerator :: CLUBS = 1, DIAMONDS = 2, HEARTS = 4, SPADES = 8
end enum
</syntaxhighlight>

=== Perl ===
<syntaxhighlight lang="perl">
my @enum = qw(CLUBS DIAMONDS HEARTS SPADES);
my( %set1, %set2 );
@set1{@enum} = ();          # all cleared
@set2{@enum} = (1) x @enum; # all set to 1
$set1{CLUBS} ...            # false
$set2{DIAMONDS} ...         # true
</syntaxhighlight>

=== Python ===
<syntaxhighlight lang="python">
from enum import Enum
class Cardsuit(Enum):
    clubs = 1
    diamonds = 2
    hearts = 3
    spades = 4
</syntaxhighlight>

=== Ruby ===
<syntaxhighlight lang="ruby">
module Cardsuit
  clubs = 1
  diamonds = 2
  hearts = 3
  spades = 4
end
</syntaxhighlight>

=== VBA ===
VBAの列挙型は自動的に"整数"データ型に代入され、それ自身データ型にもなりうる。
<syntaxhighlight lang="vb">
Option Explicit

Enum MyEnumeratedType
    myType1 = 1
    myType2 = -1
End Enum

Sub EnumExample()
    Dim a As MyEnumeratedType
    a = myType1
    MsgBox a
End Sub
</syntaxhighlight>

== 関数型言語 ==
=== Common Lisp ===
[[Common Lisp]]は、型指定子 member を用いる。例えば、
<syntaxhighlight lang="lisp">
(deftype cardsuit ()
  '(member club diamond heart spade))
</syntaxhighlight>
ここで定義された cardsuit型 は、シンボルclub、diamond、heart、spadeの集合となる。
<syntaxhighlight lang="lisp">
(typep 'club 'cardsuit) ;clubは、cardsuit型か?
;=>  T                  ;true
</syntaxhighlight>
また、上記の型定義で利用した deftype は、表記を拡張することにも用いる。
<syntaxhighlight lang="lisp">
(deftype finite-element-set-type (&rest elements)
  `(member ,@elements))
</syntaxhighlight>
は、型指定子 member に finite-element-set-type という新しい名前を付ける。
これを用いて、
<syntaxhighlight lang="lisp">
(deftype cardsuit ()
  '(finite-element-set-type club diamond heart spade))
</syntaxhighlight>
として、前述のcardsuitと同じものを定義することに利用できる。表記上似ていて紛らわしいmember関数との混同を避けることに使えるだろう。

=== ML ===
[[ML (プログラミング言語)|ML]]の血統 (例えば、[[Standard ML|SML]]、[[OCaml]]、[[Haskell]]) である[[関数型プログラミング]]言語では[[nullary constructor]]しかない[[代数データ型]]は列挙型を実装するために使うことができる。例えば(SMLシグニチャの文法):

<syntaxhighlight lang="haskell">
datatype cardsuit = Clubs | Diamonds | Hearts | Spades
type card = { suit: cardsuit; value: int }
val hand : card list
val trump : cardsuit
</syntaxhighlight>

もし、実際にそのような表現が実装に必要とされるならば、これらの言語では、小さな整数表現は完全にプログラマから隠蔽される。一方で、Haskellは型が派生でき、型と''Int''とのマッピングを得る実装ができる''Enum''[[型クラス]]を持つ。

=== Scala ===
<syntaxhighlight lang="scala">
object Cardsuit extends Enumeration {
  type Cardsuit = Value
  val Clubs, Diamonds, Spades, Hearts = Value
}
</syntaxhighlight>

== データベース ==
[[データベース]]によっては列挙型を直接サポートする。

=== MySQL ===
[[MySQL]]では、テーブルが生成されるときの文字列として指定された許容量を持つ列挙型**ENUM**が存在する。0としての空文字列とともに値は数値インデックスとして保存される。最初の文字列には1が保存され、第二の文字列には2が保存される、etc...。値は数値インデックスまたは文字列として検索し保存することができる。

=== PostgreSQL ===
[[PostgreSQL]]では **CREATE ENUM''' 構文にて列挙型を定義できる。入出力は文字列として行うが、内部的にはシステムが割り当てた 4 byte の整数として保存される。列挙型の名前が長い場合には格納の効率が高まり、数値として処理できるため検索性能も向上する。

## 脚注
### 注釈

### 出典

## 外部リンク
*Enumerated types in MySQL