0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Javaにおけるswitch式化とパターンマッチングの進化

Posted at

はじめに

パターンマッチングは、値が特定の構造を持つかどうかをチェックし、成功した場合にその構成要素を抽出する仕組みです。
主に関数型言語において実装されていましたが、近年では Ruby, Python, Java などのオブジェクト指向言語にも取り入れられています。

断り書き

個人的にパターンマッチングに興味があり、最近 Scheme と Java を初めたので Java のパターンマッチングについてまとめた記事です。

Java にまだ慣れていないのでサンプルコードは Java の記事にも関わらず Scheme(Gauche)、 Python を使っていますが、追々 Java に書き換えていくつもりです。

Java の switch 文・式

Java は C 言語風の switch 文を最初から持っていたが、面白いことに switch 式が追加された。

  • Java SE 12(プレビュー):JEP 325 で switch 式を初導入
  • Java SE 14(標準機能化):JEP 361 で正式サポート
  • Java SE 17(プレビュー):JEP 406 で switch のパターンマッチングを初導入
  • Java SE 21(標準機能化):JEP 441 で正式サポート

これによって Java は

  • switch 式の便利さ
  • 構成要素を抽出する分解

を得た。

switch 式

下記は 1~12 または "春", "夏", "秋", "冬"を入力すると対応する季節か月のリストを返す処理を switch 式で書いたもの。

Object input = "夏"; // ← ここを 1~12 または "春", "夏", "秋", "冬" に変えて試してください

Object result = switch (input) {
    case Integer month when month >= 1 && month <= 12 -> switch (month) {
        case 3, 4, 5   -> "春";
        case 6, 7, 8   -> "夏";
        case 9, 10, 11 -> "秋";
        case 12, 1, 2  -> "冬";
        default        -> "不明";
    };
    case String season -> switch (season) {
        case "春" -> java.util.List.of(3, 4, 5);
        case "夏" -> java.util.List.of(6, 7, 8);
        case "秋" -> java.util.List.of(9, 10, 11);
        case "冬" -> java.util.List.of(12, 1, 2);
        default   -> java.util.List.of();
    };
    default -> "不明な入力";
};
System.out.println(result);
//[6, 7, 8]

単純な数字だけではなく、型で分岐することができる。

式・文

switch 文が式になったと言ったが、その利点を説明するためには文と式の違いを理解する必要がある。

一言で言うと式は値を返す
文と式の違いを示すため、他の言語を例に出す。

例えば Python にもパターンマッチ機能があるが、match 文という。

# ネストしている配列を平滑化(ネストを排除)する関数
def flatten(lst:list) -> list:
    def loop(atom, acc):
        match atom:
            # 空リスト
            case []:
                return acc
            # リスト
            case [head, *tail]:
                acc = loop(head, acc)
                return loop(tail, acc)
            # 非リスト
            case _:
                return acc + [atom]
    
    return loop(lst, [])

if __name__ == "__main__":
    print(flatten([1,[2,3],4]))
    # [1, 2, 3, 4]

このようにパターンマッチは文でも便利だが、実は関数の引数にシュっと差し込むようなことはできない。

式の強力さ

式は関数に渡すことができる。Scheme を例に出す。

(display (let loop ((x 10) (acc '())) (if (= x 0) acc (loop (- x 1) (cons x acc)))))
;; (1 2 3 4 5 6 7 8 9 10)#<undef>

このように、関数の引数の中でネストさせることが容易にできる。

;; 1-10 の数を偶奇に分類する
(display
  (let loop ((x 10)
             (even '())
             (odd '()))
    (match x
      [0
       (list (cons 'even (reverse even))
             (cons 'odd (reverse odd)))]
      [_
       (if (even? x)
           (loop (- x 1) (cons x even) odd)
           (loop (- x 1) even (cons x odd)))]))
)
;; ((even 10 8 6 4 2) (odd 9 7 5 3 1))#<undef>

ロジックを組み込み、偶数・奇数に分類するといった多少込み入った処理も簡単に表現できる。

分解

パターンマッチによって構成要素を引き回すことができる。

;;;; 二分木を走査するプログラム
(define tree1 '(5 (4 () ()) (7 () ())))         

;; パターンマッチで巡回
;; 葉の数字を抜き出す
(define (tree-match tree)
  (flatten (match tree
         [() '()]
         [(A B C)
            (list A (tree-match C) (tree-match B))])
         ))

(display (tree-match tree1))
;; (5 7 4)
;;;; Gauche 版 flatten
(use util.match)
(define (flatten lst)
  (let loop ((atom? lst)
             (acc '()))
    (match atom?
      [(? null? atom?) acc]
      [(? pair? atom?)
        (loop (car atom?)
          (loop (cdr atom?)
            acc))]
      [_ (cons atom? acc)])))

 (display (flatten '(1 (2 3) (4 (5 6)))))
 ;; => (1 2 3 4 5 6)#<undef>

このように、パターンマッチは構造化されたデータを直感的かつ宣言的に処理する強力なツールとなる。

終わりに

本記事では、「式と文の違い」「パターンマッチによる構造の分解」「Java における switch の式化とパターンマッチの進化」について概観した。

Java においては、switch が式になったことで、関数型言語に近い表現が可能になりつつある。加えて、instanceof の改良やガード付きパターンなど、Java の進化はまだ途中でありながら、確実に「表現力」を手にしつつある。

後でやること

  • 変化が起きているので、キャッチアップしたら switch の構文についての説明を追加
    • 9 月にリリースされる Java SE 25 が LTS
  • サンプルコードが Scheme, Python であるので追々修正する
  • instanceof パターンマッチ

Link

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?