洋食の日記

「た・である」調ではなく「です・ます」調で書きはじめれば良かったなと後悔してる人のブログです

Rubyのパイプラインを考えてみた

はじめに

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 👌