ko1 / rubyhackchallenge

472 stars 83 forks source link

Procに関数合成を実装する #13

Open yuroyoro opened 7 years ago

yuroyoro commented 7 years ago

概要

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 :<<, :composealias_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)
riseshia commented 7 years ago

おーどこかでみたcompositionだ!!!

shunsakai commented 7 years ago

この辺読んだことあります! http://yuroyoro.hatenablog.com/entry/2012/08/10/232443

yuroyoro commented 7 years ago

はい、この記事と、以前作ったgemをCレベルで実装してみます

yuroyoro commented 7 years ago

実装 : https://github.com/yuroyoro/ruby/pull/7

yuroyoro commented 7 years ago

5年前にどの記法を採用するかで揉めてる…

https://bugs.ruby-lang.org/issues/6284

yuroyoro commented 7 years ago

上記のIssueにcommented.

https://bugs.ruby-lang.org/issues/6284#note-50