はじめに
Rubyにパイプライン演算子が追加されるかも?というのが、少し前に話題になった。他方、R言語にはmagrittrというパイプラインを実現するパッケージがあり、これが便利だ。
# h(g(f(x))) x %>% f %>% g %>% h
データ分析では、カテゴリ変数を整数値化して欠損値を補ってアレしてコレして〜など、データを段階的に変換する処理がよく発生する。そういった処理が、magrittrでスッキリ書ける。これがRubyにも欲しい。
やってみた
パイプラインを実現するために、ひとまず「自らを第一引数としてメソッドを呼び出す」ことができれば良さそうだ(magrittrではドットで任意の引数に入れることができるが、今回は見送る)。 これは、以下のようにシンプルに書ける。受け取ったメソッドfnを、自らを第一引数としてsendで呼び出す。メソッド名は、絵文字でそれっぽいのにした。
module Pipeline def 👉(fn, *args) send(fn, self, *args) end end
これをStringで使ってみる。scatメソッドは、スペースをはさんで文字列を連結する。
class String include Pipeline end def scat(a, b) "#{a} #{b}" end "Hello" .👉(:scat, "world") .👉(:scat, "!!") .👉(:puts)
これを実行すると、次のようになる。Helloにworldが連結され、それにさらに!!が連結され、putsで出力されている。
Hello world !!
数値もいける。
class Numeric include Pipeline end def add(a, b) a + b end 1 .👉(:add, 2) .👉(:add, 0.5) .👉(:puts) # 1 + 2 + 0.5 で 3.5 が表示される
もうKernelにぶっこんじゃえばいいかも。
module Kernel def 👉(fn, *args) send(fn, self, *args) end end
メソッド名の指定にシンボルを使っているのと、👉の手前にドットがあるのが、演算子っぽくない。TracePointとか黒魔術を試みたけど、なんか上手くいかなかった。やってるうちに、そもそも演算子じゃないからコレで良いかな、という心になってしまった。途中に、パイプラインと関係ないメソッドを挟んでも、自然に見えるので。
"Hello" .👉(:scat, "world") .👉(:scat, "!!") .upcase .👉(:puts) # HELLO WORLD !! と表示される
おわりに
こだわれば、もっと複雑で高度なモノにできると思う。ひとまず、データの前処理がmagrittrぽく書けるからOK 👌