先日ポストしたScalaっぽいパターンマッチをRubyで実装するをベースに 一通り機能を揃えてライブラリ化した(pattern-match)。 面白そうなパターンをいくつか例に取ってみると、まず多重代入。

match([0, [1, 2, 3, 4]]) {
  with(_[a, _[b, *c, d]]) { # `Array.(a, Array.(b, *c, d))'と同じ
    p [a, b, c, d] #=> [0, 1, [2, 3], 4]
  }
}

Gaucheのutil.match由来の___、__k。

match([[0, 1], [2, 3]]) {
  with(_[_[a, b], ___]) {
    p [a, b] #=> [[0, 2], [1, 3]]
  }
}

赤黒木のbalance(参考までにScala版実装)。

Node = Struct.new(:left, :key, :right)
class R < Node; end
class B < Node; end

def balance(left, key, right)
  match([left, key, right]) {
    with(_[R.(a, x, b), y, R.(c, z, d)]) { R[B[a, x, b], y, B[c, z, d]] }
    with(_[R.(R.(a, x, b), y, c), z, d]) { R[B[a, x, b], y, B[c, z, d]] }
    with(_[R.(a, x, R.(b, y, c)), z, d]) { R[B[a, x, b], y, B[c, z, d]] }
    with(_[a, x, R.(b, y, R.(c, z, d))]) { R[B[a, x, b], y, B[c, z, d]] }
    with(_[a, x, R.(R.(b, y, c), z, d)]) { R[B[a, x, b], y, B[c, z, d]] }
    with(_) { B[left, key, right] }
  }
end

特殊なパターンとしてDuck Typing的なことも出来るようにしてみた。ただ、実用性があるかというと、どうなんだろう……。

match(10) {
  with(Object.(:to_i => a, :foobar => b)) { :not_match }
  with(Object.(:to_i => a, :to_s.(16) => b)) {
    p [a, b] #=> [10, "a"]
  }
}

ちなみに、ruby-trunk - Feature #4085 : Refinements and nested methodsをサポートする環境であれば、 次のような書き方も出来るようになっている1。Refinements素晴らしい。

def balance(left, key, right)
  match([left, key, right]) {
    with(_[R[a, x, b], y, R[c, z, d]]) { R[B[a, x, b], y, B[c, z, d]] }
    with(_[R[R[a, x, b], y, c], z, d]) { R[B[a, x, b], y, B[c, z, d]] }
    with(_[R[a, x, R[b, y, c]], z, d]) { R[B[a, x, b], y, B[c, z, d]] }
    with(_[a, x, R[b, y, R[c, z, d]]]) { R[B[a, x, b], y, B[c, z, d]] }
    with(_[a, x, R[R[b, y, c], z, d]]) { R[B[a, x, b], y, B[c, z, d]] }
    with(_) { B[left, key, right] }
  }
end
  1. r29944向けパッチで動作確認済み