先日ポストした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向けパッチで動作確認済み
Before...
_ shugo [> 外側で得た変数を内側のwithの引数として渡しても、値の参照ではなく常にvariable pattern相当にな..]
_ k_tsj [なるほど、分かりました。 やはり両方ともmatchのネストが原因のようです。 GitHub上のmasterに..]
_ shugo [ありがとうございました。後で確認してみます]