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

きくらげ観察日記

好きなことを、適当に。

Gaucheでパターンマッチ

util.matchモジュールを使えば、GaucheでもHaskellのようなパターンマッチを行うことができます。

リテラルに対するパターンマッチ

リテラルに対するパターンは、そのリテラルを直接書けばOKです。

(use util.match)
(define (fact n)
  (match n
         (0 1)
         (n (* n (fact (- n 1))))))
(fact 5) ; => 120

ペア、リストに対するパターンマッチ

ペアとリストに関しては、比較的直感的にパターンを記述することができます。

(define (head xs)
  (match xs
         (() (error "empty list"))
         ((x rest ...) x)))

(head '(1 2 3 4 5 6)) ; => 1
(head '())            ; => ERROR: empty list

上のheadは次のように書くこともできます。

(define (head2 xs)
  (match xs
         (() (error "empty list"))
         ((x . rest) x)))

もちろん(x . rest)はxとrestのペアを表すパターンなので、ペアに対してマッチさせることもできます。

(define (norm p)
  (match p
         ((x . y) (sqrt (+ (expt x 2) (expt y 2))))))

(define p (cons 3 2))

(norm p) ; => 3.6055...

クラスに対するパターンマッチ

クラスに対しても$という特殊な記号を使うことでパターンマッチを行うことができます。

(define-class <just> ()
  ((value :init-keyword :value)))
(define-class <nothing> () ())

(define (get-name maybe-name)
  (match maybe-name
         (($ <just> name) name)
         (($ <nothing>) "Anonymous")))

(get-name (make <just> :value "Taro")) ; => "Taro"
(get-name (make <nothing>))            ; => "Anonymous"

また、@という記号を用いることでクラスのスロット名を指定してパターンマッチを行うこともできます。

(define-class <point> ()
  ((x :init-keyword :x)
   (y :init-keyword :y)))

(define (point-norm p)
  (match p
         ((@ <point> (x x-value) (y y-value))
          (sqrt (+ (expt x-value 2) (expt y-value 2))))))

(define p (make <point> :x 5 :y 5))

(point-norm p) ; => 7.0710...

その他のマクロ

他にも、代入の左辺をパターンにできるmatch-letや、引数をパターンにできるmatch-define, match-lambdaなどがあります。