Gauche:matchの応用

Gauche:matchの応用

Shiro(2010/05/23 16:21:25 PDT): 応用というと大げさだけど、繰り返し質問が上がる、ちょっと突っ込んだ使い方についてのメモ。

Haskellのasパターンみたいに、部分構造とその上位構造の両方にバインドしたいんだけど

たとえばこんな:

case val of
  (p@(x:xs),_) -> ...
  ...

これだと、valが2要素のタプルで最初の要素がペアだった場合に、 最初の要素全体がpに、pの中の先頭要素がxに、残りがxsにバインドされる。

util.matchではandを使えば同じことができる。つまり、「pと x:xsの両方が ともにマッチした場合」と考える。

(match val
  [((and (x . xs) p) _) ...]
  ...)

先頭は (and p (x . xs)) と書いても機能的には同じ。 ただ、より厳しい条件を先に書いておいた方が分岐が捨てられるのが速いから 気分的にはその方がきもちいいかなと。

まあ、util.matchは展開時に最適化を行ってくれるので、

(match val
  [((and p (x . xs)) _) ...]
  [(p _) ...])

となっていた場合、両者に共通する「valが2要素のリストであること」は 一度しかチェックされない。なのであまり神経質になる必要はないのだけれど。

ガードが欲しい、つまりパターンでマッチをかけたあとに、より高度な条件判断でマッチの成否を分ける機能が欲しい

ちょっとわざとらしい例だけど、たとえば等しい長さのOrd aのリストについて 先頭要素優先で比較する:

lt [] []               = False
lt (a:_) (b:_) | a < b = True
               | a > b = False
lt (_:as) (_:bs)       = lt as bs

先頭要素が等しい時は2番目のマッチがfailして3番目のマッチに進む。

util.matchには特別な「ガード部」を書くことは出来ないけれど、もっと柔軟な 仕組みがある。

(define lt
  (match-lambda*
   [(() ()) #f]
   [((a . _) (b . _)) (=> break) (cond [(< a b) #t] [(> a b) #f] [else (break)])]
   [((_ . as) (_ . bs)) (lt as bs)]))

2番目のマッチ節の (=> break) というのが、breakに「失敗継続」を束縛するマッチ構文。 失敗継続とは、「このマッチ節が失敗した場合に実行される内容」だ。

したがってマッチ節のアクションの中で好きなだけ計算を行い、 「やっぱだめだ」となったら失敗継続を呼び出せば、2番目のマッチ節は マッチしなかったことになって続くマッチ節が試される。

議論、コメント

Post a comment

Name:

Tag: util.match


Last modified : 2013/04/12 23:30:11 UTC