列挙型(れっきょがた、enumerated typeあるいはenumeration type)とは、コンピュータプログラミングにおいて、プログラマが選んだ各々の識別子(列挙子)をそのまま有限集合として持つ抽象データ型である。列挙型は一般に、カードのスートのように番号順を持たないカテゴリ変数として使われるが、実際のコンパイル時あるいは実行時には、列挙型は整数で実装されることが多い。各々の識別子は通例異なる整数値を持つが、複数の識別子に対して意図的に同じ整数値を割り当てる(つまり別名を定義する)ことも可能である。
また列挙型は、整数を使用する場合と比較して、明示的にマジックナンバーを使用するよりもプログラムソースの可読性を改善するのに役立つ。言語によっては、列挙型の整数表現はプログラマに見えないようになっていることもあり、これによりプログラマが列挙値に対して算術演算を行うような乱用を防いでいる。列挙型の定数は同じ列挙型の変数にしか代入できない(整数型の変数に代入するには明示的な型変換などが必要)という仕様になっている言語もある。
プログラミング言語によっては、真偽値の論理型は、あらかじめ宣言された二値の列挙型とされている。
C言語では構造体および共用体のアナロジーとして、列挙体(enumeration)とも呼ばれる。
Pascalおよび類似言語
Pascal
Pascalでは、列挙型は括弧で括られた値のリストによって暗黙的に宣言できる。
複数の変数に対して使うことができるように、列挙型の宣言は、typeを使った型のシノニム(別名)宣言に現れることが多い。
列挙値の順序は書いた順番になる。列挙型は順序型 ordinal type であり、Pred、Succ関数は、列挙の前または次の値を与え、 Ordは列挙値を整数表現に変換する。しかし、標準Pascalでは数値型から列挙型への変換はできない。拡張Pascalは拡張されたSucc関数経由でこの機能性を提供する。他のPascal派生言語の一部では、型キャストで変換可能なものもある。
Ada
Adaでは、Pascalとよく似た定義となるが"="の代わりに"is"を用いる。
属性Pred, Succ, ValおよびPosに加えて、Adaにはさらに属性ImageおよびValueがあり、文字列変換を行うことができる(Image: 列挙値から文字列への変換、Value: 文字列から列挙値への変換)。
Cスタイルの言語と同様に、Adaでは列挙値として数値に何を用いるかを指定することができる。
Cスタイルの言語と異なり、Adaでは列挙値に割り当てるビット数を指定することができる。
さらに列挙値を配列の添字として用いることもできる。
Modula-3と同様に、AdaではBooleanやCharacterは列挙型の一種に過ぎない(パッケージ"Standard"で既定義)。Modula-3と異なり、Adaでは自前の文字型も定義することができる。
Cおよび構文的に類似の言語
Cの構文を受け継ぐ言語はほとんどが列挙型をサポートする。ただしPerlやJavaScriptなどの動的型付け言語は一般に列挙型をもたない。
C言語
C言語のオリジナルのK&Rに列挙型は存在しなかったが、ANSI標準 (C89) で追加された。Cでは、列挙体はenumキーワードを使った明示的な定義によって生成できる。enumによって定数群を宣言することができるが、constと違って記憶域の割り当てを伴わない。これは構造体と共用体宣言を連想させる。
Cは列挙値の"小さな整数"表現を直接プログラマに公開する。Cの列挙体は整数型の一種である。整数値と列挙値は自由に変換可能であり、列挙値でも全ての数値演算が可能となっている。結果として、列挙体に定義されていない値すら取りえることもある。事実、言語仕様によると、上記のコードはint型の定数としてCLUBS、DIAMONDS、HEARTS、SPADESを定義しているが、これらがその型の変数に保存されるときは、(暗黙裡に)enum cardsuitに変換されるだけである。
typedefによりエイリアスを定義することも可能である。
Cでは、プログラマが明示的に列挙定数の値を指定することも可能である。例えば、
は、スートの数学的な集合をビット演算によってenum cardsuitとして表現することを可能にする型を定義する目的で使える。
値を指定しなかった場合、最初の列挙定数の値は0となる。後続の列挙定数の値は、直前の列挙定数の値を1だけインクリメントしたものとなる。
型名を持たない無名の列挙体を定義することも可能である。
標準Cでは空の構造体が定義できないのと同様に、空の列挙体も定義できない。
C
C はCから直接引き継いだ列挙体(列挙型)を持っている。しかしCとは異なり列挙型は整数型ではない。列挙定数はint型ではなく基の列挙型となる(ただしint型へ昇格できる。整数型から列挙型への変換には明示的なキャストが必要である)。このため、整数型と列挙型との間で多重定義できる。また、列挙型は利用者定義型の一種であるということから、列挙型に対しての演算子多重定義も可能となっている。
C言語とは異なり、C では曖昧さがない限りenumキーワードで修飾せずとも列挙型をそのまま型名として使用できる。これは構造体や共用体の使用時にstructやunionキーワードによる修飾が不要であるのと同じである。
C では空の構造体が定義できるのと同様に、空の列挙体も定義できる。
C 11では、スコープ付きの強く型付けされた列挙型(enum classもしくはenum struct)が新たに実装された。
C#
C#の列挙型はSystem.Enumから暗黙的に派生する値型であり、Cのenumの意味する多くの"小さな整数"を保持する。いくつかの数値演算はenumでは定義されないが、enum値は明示的に整数値に型変換することができ、また整数値から元に戻すこともできる。またenum変数はenum宣言によって定義されなかった値を保存できる。例として、
が与えられたとき、式Diamonds 1とHearts - Clubsは直接許可される(ループで一連の値を走査することや、二つのenumの間にいくつのステップがあるか計算することはありうる)が、Hearts * Spadesは理にかなっていないと考えられ、値が最初に整数に変換されるということだけが許可される。
また、FlagsAttributeを指定することで、ビット演算が許可される。
Groovy
Groovyは動的型付け言語だが、Java由来の列挙型をサポートする。
Java
JavaはJava SE (J2SE) version 5.0から列挙型を導入した。
Javaの型システムは、整数から分離された型として列挙を扱うが、(Enum.ordinal()メソッドを使用してenum値の整数表現を取得できることを除き)enumと整数値との混合演算は許されていない。実際には、Javaのenum型は現に、数値型というよりもむしろ、コンパイラによって生成された特殊なクラスである。enum値はそのクラスのあらかじめ生成されたグローバルなインスタンスとして振る舞う。enum型はインスタンスメソッドとコンストラクタ(引数が各々のenum値を分割指定できる)を持つ。全てのenum型は暗黙のうちにEnum抽象クラスを継承している。enum型を直接インスタンス化することは許可されない。各列挙型には、静的メソッドvalues()が暗黙的に定義され、列挙型に含まれるすべての定数の集合を配列として取得することができる。
Kotlin
TypeScript
その他の手続き型言語
Fortran
Perl
Python
Ruby
VBA
VBAの列挙型は自動的に"整数"データ型に代入され、それ自身データ型にもなりうる。
関数型言語
Common Lisp
Common Lispは、型指定子 member を用いる。例えば、
ここで定義された cardsuit型 は、シンボルclub、diamond、heart、spadeの集合となる。
また、上記の型定義で利用した deftype は、表記を拡張することにも用いる。
は、型指定子 member に finite-element-set-type という新しい名前を付ける。 これを用いて、
として、前述のcardsuitと同じものを定義することに利用できる。表記上似ていて紛らわしいmember関数との混同を避けることに使えるだろう。
ML
MLの血統 (例えば、SML、OCaml、Haskell) である関数型プログラミング言語ではnullary constructorしかない代数データ型は列挙型を実装するために使うことができる。例えば(SMLシグニチャの文法):
もし、実際にそのような表現が実装に必要とされるならば、これらの言語では、小さな整数表現は完全にプログラマから隠蔽される。一方で、Haskellは型が派生でき、型とIntとのマッピングを得る実装ができるEnum型クラスを持つ。
Scala
データベース
データベースによっては列挙型を直接サポートする。
MySQL
MySQLでは、テーブルが生成されるときの文字列として指定された許容量を持つ列挙型ENUMが存在する。0としての空文字列とともに値は数値インデックスとして保存される。最初の文字列には1が保存され、第二の文字列には2が保存される、etc...。値は数値インデックスまたは文字列として検索し保存することができる。
PostgreSQL
PostgreSQLでは CREATE ENUM 構文にて列挙型を定義できる。入出力は文字列として行うが、内部的にはシステムが割り当てた 4 byte の整数として保存される。列挙型の名前が長い場合には格納の効率が高まり、数値として処理できるため検索性能も向上する。
脚注
注釈
出典
外部リンク
- Enumerated types in MySQL
![[第15回] 列挙型(Enum)の使い方を学ぶ|Unityで学ぶC入門 XRHub](https://xr-hub.com/wp-content/uploads/2019/09/列挙型変数day.png)
