rubyhackchallenge
rubyhackchallenge copied to clipboard
Procに関数合成を実装する
概要
Proc#compose
でProc同士を合成できるようにする。
f.compose g
で、 Proc{|x| f.call(g.call(x)) }
となるProcを生成する
f = Proc.new{|x| x + 1 }
g = Proc.new{|x| x * x }
f.compose(g).call(2) #=> 5
雑な実装イメージはこんな感じ
class Proc
def compose(f)
Proc{|x| self.call(f.to_proc.call(x)) }
end
end
逆方向の関数合成のサポート
さらに、 Proc#reverse_compose
で 逆の関数合成もできるようにする。 f.reverse_compose(g).call(x) == g.compose(f).call(x)
となる。
Proc#>>
と Proc#<<
にaliasする
alias_method :<<, :compose
と alias_method :>>, :reverse_compose
にaliasしておくことで利便性が向上する
Symbol
への拡張
Symbol
にも compose
および reverse_compose
を実装する。 実装はself.to_proc.compose
するだけ
モチベーション
以下のようなコードは、関数合成があればすっきりと記述できる
arr = [:foo, :bar, :baz]
# arrをto_sしてupcaseする
# blockを書く場合
arr.map{|v| v.to_s.upcase }
# mapを2回書く場合
arr.map(&:to_s).map(&:upcase)
# composeを使うとこのように書ける
arr.map(&:to_s.to_proc.compose(&:upcase))
# さらに、 >> を使うと呼び出しの流れを可視化できる
arr.map(&:to_s.to_proc >> :upcase.to_proc)
# Symbolに >> が実装されていればさらにすっきりする
arr.map(&:to_s >> :upcase)
おーどこかでみたcompositionだ!!!
この辺読んだことあります! http://yuroyoro.hatenablog.com/entry/2012/08/10/232443
はい、この記事と、以前作ったgemをCレベルで実装してみます
実装 : https://github.com/yuroyoro/ruby/pull/7
5年前にどの記法を採用するかで揉めてる…
https://bugs.ruby-lang.org/issues/6284
上記のIssueにcommented.
https://bugs.ruby-lang.org/issues/6284#note-50