pattern-match gem
先日ポストした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
-
r29944向けパッチで動作確認済み ↩