コンテンツにスキップ

プログラミング/列挙型

出典: フリー教科書『ウィキブックス(Wikibooks)』

プログラミングにおいてデータ型は重要な要素です。データ型を正しく理解することで、効率的なコード作成やバグの防止が可能になります。その中でも列挙型は、特定の値の集合から選択できる便利なデータ型です。

列挙型は、決まった値以外を選べないよう制約を設けることで、入力ミスを防ぎます。また、コードの可読性や保守性を向上させるためにも役立ちます。

この節では、列挙型の基本概念、使用方法、実際の活用例について学び、より洗練されたコード作成を目指します。

列挙型とは

[編集]

プログラミングでは、データの種類を明確にする必要があります。数字、文字列、真偽値など、それぞれに異なる処理が必要だからです。

列挙型は、あらかじめ定義された複数の値から一つを選択するデータ型です。例えば、「曜日」を表す列挙型では、「月曜日」「火曜日」「水曜日」などが選べます。列挙型を使うことで、特定の値しか取れないことが保証され、プログラムの品質向上やバグの削減に役立ちます。

列挙型の定義方法

[編集]

列挙型は、プログラミング言語によって定義方法が異なりますが、一般的には以下のような形式で定義します。

enum 列挙型名 {
  列挙子1,
  列挙子2,
  列挙子3,
  ...
}

ここで、「列挙型名」は定義する列挙型の名前を、「列挙子1」「列挙子2」「列挙子3」といったものは、列挙型が取りうる値を定義するものです。

例えば、以下のように「曜日」を表す列挙型を定義することができます(プログラミング言語はJava)。

enum DayOfWeek {
  Monday,
  Tuesday,
  Wednesday,
  Thursday,
  Friday,
  Saturday,
  Sunday
}

列挙型名は「DayOfWeek」とし、それぞれの列挙子には曜日の名前を設定しています。

列挙型の使用方法

[編集]

列挙型を使用する際には、定義した列挙型の名前と列挙子を指定します。

例えば、上記の「DayOfWeek」列挙型を使用する場合には、以下のように記述します。

DayOfWeek today = Wednesday;

変数「today」には「Wednesday」という値が設定されます。

列挙型と網羅性の担保

[編集]

列挙型で定義された列挙値をswitchや類似の制御構造で網羅していなかった場合、コンパイラ等は静的に網羅性の担保を支援することができます。

Rust

Rustは、列挙型(enum)とマッチング(match)構文を組み合わせて、パターンマッチングを実現しています。これにより、全てのパターンを網羅するかどうかをコンパイル時に確認することができます。また、Rustには、コンパイル時に安全性をチェックするための静的解析ツールであるClippyがあり、列挙型のパターンマッチングにおいて未網羅のパターンを検出することができます。

// 列挙型(enum) Suitを定義
enum Suit {
    Heart, // ハートのスート
    Diamond, // ダイヤのスート
    Club, // クラブのスート
    Spade, // スペードのスート
}

// スートを出力する関数 print_cardを定義
fn print_card(suit: Suit) {
    // 引数で渡されたスートに応じてメッセージを出力
    match suit {
        Suit::Heart => println!("This is a heart."), // Heartの場合
        Suit::Diamond => println!("This is a diamond."), // Diamondの場合
        Suit::Club => println!("This is a club."), // Clubの場合
        Suit::Spade => println!("This is a spade."), // Spadeの場合
    }
}

// main関数で4つの異なるスートを出力する
fn main() {
    print_card(Suit::Heart); // ハートのスートを出力
    print_card(Suit::Diamond); // ダイヤのスートを出力
    print_card(Suit::Club); // クラブのスートを出力
    print_card(Suit::Spade); // スペードのスートを出力
}
Swift

Swiftは、列挙型(enum)とスイッチ(switch)文を組み合わせて、全てのケースを網羅していることをコンパイラが確認する機能を持っています。また、Swiftでは、列挙型に対して、コードの可読性を高めるためのエイリアスを定義することができます。

// スートの種類を定義するenum型
enum Suit {
    case heart
    case diamond
    case club
    case spade
}

// スートを受け取って、そのスートに応じてカードの種類を出力する関数
func printCard(suit: Suit) {
    switch suit {
    case .heart:
        print("This is a heart.")
    case .diamond:
        print("This is a diamond.")
    case .club:
        print("This is a club.")
    case .spade:
        print("This is a spade.")
    }
}

// カードの種類を出力する関数を、各スートで呼び出す
printCard(suit: .heart)
printCard(suit: .diamond)
printCard(suit: .club)
printCard(suit: .spade)
F#

F#は、列挙型(enum)とパターンマッチング(pattern matching)を組み合わせて、全てのパターンを網羅していることを確認する機能を持っています。また、F#では、列挙型に対して、マップやリストなどのデータ構造を組み合わせることができます。

// スートを表す列挙型を定義する
type Suit =
    | Heart      // ハート
    | Diamond    // ダイヤ
    | Club       // クラブ
    | Spade      // スペード

// スートに基づいてカードを印刷する関数を定義する
let printCard suit =
    match suit with
    | Heart -> printfn "This is a heart."   // ハートの場合
    | Diamond -> printfn "This is a diamond."   // ダイヤの場合
    | Club -> printfn "This is a club."     // クラブの場合
    | Spade -> printfn "This is a spade."   // スペードの場合

// カードを表示する
printCard Heart
printCard Diamond
printCard Club
printCard Spade
Kotlin
// トランプのカードのスートを表す列挙型
enum class Suit {
    HEART,    // ハート
    DIAMOND,  // ダイヤ
    CLUB,     // クラブ
    SPADE     // スペード
}

// スートに応じてカードを表示する関数
fun printCard(suit: Suit) {
    // スートに応じて処理を分岐
    when (suit) {
        Suit.HEART -> println("This is a heart.")    // ハートの場合
        Suit.DIAMOND -> println("This is a diamond.")// ダイヤの場合
        Suit.CLUB -> println("This is a club.")      // クラブの場合
        Suit.SPADE -> println("This is a spade.")    // スペードの場合
    }
}

fun main() {
    // カードを表示する
    printCard(Suit.HEART)    // ハートのカードを表示する
    printCard(Suit.DIAMOND)  // ダイヤのカードを表示する
    printCard(Suit.CLUB)     // クラブのカードを表示する
    printCard(Suit.SPADE)    // スペードのカードを表示する
}
Java
// 列挙型 Suit を定義する
enum Suit {
    HEART,    // ハート
    DIAMOND,  // ダイヤ
    CLUB,     // クラブ
    SPADE     // スペード
}

class Main {
    // printCard メソッドを定義する
    static void printCard(Suit suit) {
        // 引数で受け取った suit に応じて分岐する
        switch (suit) {
            case HEART:  // suit が HEART の場合
                // ハートであることを表示する
                System.out.println("This is a heart.");
                break;  // 分岐から抜ける
            case DIAMOND:  // suit が DIAMOND の場合
                // ダイヤであることを表示する
                System.out.println("This is a diamond.");
                break;  // 分岐から抜ける
            case CLUB:  // suit が CLUB の場合
                // クラブであることを表示する
                System.out.println("This is a club.");
                break;  // 分岐から抜ける
            case SPADE:  // suit が SPADE の場合
                // スペードであることを表示する
                System.out.println("This is a spade.");
                break;  // 分岐から抜ける
        }
    }

    // main メソッドを定義する
    public static void main(String[] args) {
        // printCard メソッドを呼び出して、それぞれのトランプのマークを表示する
        printCard(Suit.HEART);
        printCard(Suit.DIAMOND);
        printCard(Suit.CLUB);
        printCard(Suit.SPADE);
    }
}
Go
package main

import "fmt"

// 列挙型 Suit を定義
type Suit int

const (
    Heart Suit = iota // ハートのスート
    Diamond           // ダイヤのスート
    Club              // クラブのスート
    Spade             // スペードのスート
)

// スートを出力する関数 print_card を定義
func print_card(suit Suit) {
    // 引数で渡されたスートに応じてメッセージを出力
    switch suit {
    case Heart:
        fmt.Println("This is a heart.")
    case Diamond:
        fmt.Println("This is a diamond.")
    case Club:
        fmt.Println("This is a club.")
    case Spade:
        fmt.Println("This is a spade.")
    }
}

// main関数で4つの異なるスートを出力する
func main() {
    print_card(Heart)   // ハートのスートを出力
    print_card(Diamond) // ダイヤのスートを出力
    print_card(Club)    // クラブのスートを出力
    print_card(Spade)   // スペードのスートを出力
}
Scala
// 列挙型 Suit を定義
object Suit extends Enumeration {
  type Suit = Value
  val Heart, Diamond, Club, Spade = Value
}

// スートを出力する関数 print_card を定義
def print_card(suit: Suit.Suit): Unit = {
  // 引数で渡されたスートに応じてメッセージを出力
  suit match {
    case Suit.Heart   => println("This is a heart.")
    case Suit.Diamond => println("This is a diamond.")
    case Suit.Club    => println("This is a club.")
    case Suit.Spade   => println("This is a spade.")
  }
}

// main関数で4つの異なるスートを出力する
def main(args: Array[String]): Unit = {
  print_card(Suit.Heart)   // ハートのスートを出力
  print_card(Suit.Diamond) // ダイヤのスートを出力
  print_card(Suit.Club)    // クラブのスートを出力
  print_card(Suit.Spade)   // スペードのスートを出力
}
Haskell
-- 列挙型 Suit を定義
data Suit = Heart | Diamond | Club | Spade
    deriving (Show)

-- スートを出力する関数 print_card を定義
print_card :: Suit -> IO ()
print_card suit = case suit of
    Heart   -> putStrLn "This is a heart."
    Diamond -> putStrLn "This is a diamond."
    Club    -> putStrLn "This is a club."
    Spade   -> putStrLn "This is a spade."

-- main関数で4つの異なるスートを出力する
main :: IO ()
main = do
    print_card Heart   -- ハートのスートを出力
    print_card Diamond -- ダイヤのスートを出力
    print_card Club    -- クラブのスートを出力
    print_card Spade   -- スペードのスート

以上のように、これらのプログラミング言語では、列挙型を使った網羅性の担保を支援する機能が提供されています。

歴史

[編集]

列挙型は、プログラミング言語やデーターベース管理システムにおいて、複数の異なる定数を1つの集合として定義したデーター型です。

古くは、Algol Hの言語定義に現れますが、辿ることが可能な初見の1つにPascalの(スカラー型から分岐した)列挙型が挙げられます。

program EnumExample(output); 
type
  TSeassons = (Spring, Summer, Autumn, Winter);  
var
  seasson: TSeassons;
begin
  Writeln('seasson --> Ord(seasson)');
  Writeln('------------------------');
  for seasson := Spring to Winter do
    Writeln(seasson, ' --> ', Ord(seasson));
end.
実行結果
seasson --> Ord(seasson)
------------------------
Spring --> 0
Summer --> 1
Autumn --> 2
Winter --> 3

用語集

[編集]
  • データ型(data type):プログラム内で扱うデータの種類を表す型。
  • 列挙型(enum):あらかじめ定義された一連の値の中から1つを選ぶことができるデータ型。
  • プログラム(program):コンピュータに実行させるための命令の集まり。
  • バグ(bug):プログラムの実行中に発生するエラーのこと。
  • 読みやすさ(readability):コードが容易に理解できること。
  • 保守性(maintainability):プログラムが変更された場合に、容易に修正できること。
  • 定義方法(definition):列挙型を定義するための方法。
  • 列挙子(enumerator):列挙型が取りうる値を定義するもの。
  • Java:オブジェクト指向プログラミング言語の一種。
  • 指定する(specify):明確に示すこと。
  • 網羅性(exhaustiveness):全ての場合分けが正確に記述されていること。
  • 静的に(statically):コンパイル時に判断されること。
  • Rust:システムプログラミング言語の一種。
  • マッチング(matching):値がどのパターンに合致するかを比較すること。
  • パターンマッチング(pattern matching):値があるパターンに合致するかを判定し、適切な処理を行うこと。