読者です 読者をやめる 読者になる 読者になる

pattern-match-lambda

Scheme 用のライブラリ pattern-match-lambda を作ってみました。

https://github.com/SaitoAtsushi/pattern-match-lambda

pattern-match-lambda は case-lambda よりも複雑なパターンマッチを実現する構文です。 アリティだけで区別する case-lambda と違って引数の構造 (リストやベクタ) の形、更にリテラルにもマッチさせることが出来るのが特徴です。

R7RS ポータブルに記述していますが define-library でライブラリにまとめているのを除けばマクロ定義そのものは R6RS としても使えます。 また、 R5RS でも利用できます。

[追記] その後のバージョンアップによって R6RS と非互換な機能を使ってしまっています。[/追記]

構文の形式は以下のようなものです。

(pattern-match-lambda (literals ...) (pattern expr)  ...)

与えられた引数を各 pattern と記述順に比較し、一致する場合は対応する expr を評価してその値を返します。 もしマッチする pattern が複数あったとしても最初にマッチした pattern に対応する expr のみが評価されます。 最後の節の pattern が else であった場合には他の全てにマッチしなかった場合を記述できます。 else 節がなく、全ての pattern にマッチしなかった場合の戻り値は未定義です。

(define example
  (pattern-match-lambda ()
    ((x y z) (list 'case1 x y z))
    ((x (y z)) (list 'case2 x y z))
    (((x y) z) (list 'case3 x y z))
    (else 'case4)))

(example 1 2 3)       ;; ⇒ (case1 1 2 3)
(example 4 '(5 6))    ;; ⇒ (case2 4 5 6)
(example '(7 8) 9)    ;; ⇒ (case3 7 8 9)
(example 10 11 12 13) ;; ⇒ case4

シンボルにマッチさせたい場合にはそれを第一引数に与えて下さい。
この仕様は syntax-rules の第一引数とよく似ています。

(define example2
  (pattern-match-lambda (foo bar baz)
    ((foo 1) 'foo-case-1)
    ((foo 2) 'foo-case-2)
    ((foo (x #(y z))) (list 'foo-case x y z))
    ((bar x) (list 'bar-case x))
    ((baz x) (list 'baz-case x))
    (else 'else-case)))

(example2 'foo 1)           ;; ⇒ foo-case-1
(example2 'foo '(1 #(2 3))) ;; ⇒ (foo-case 1 2 3)
(example2 'foo 2)           ;; ⇒ foo-case-2
(example2 'baz 4)           ;; ⇒ (baz-case 4)

このマクロの展開形はあまり効率のよいものではありません。 活用できる例としては、関数がパターンマッチを持つような他言語 (例えば Haskell) での定義を愚直に再現したいような場合が考えられます。

(define fact
  (pattern-match-lambda ()
   ((0) 1)
   ((n) (* n (fact (- n 1))))))

Document ID: 1d2b9086edb2e59fda98bb87ee976728