ruby func org
TRANSCRIPT
第一級関数
rubyでは意外と身近ブロックの考え方に近い
[1..100].map {|i| i * 2}
#=> [2,4,6,8,10...........198.200]
リストのそれぞれに iを引数にi * 2する関数を適用しているイメージ
Procオブジェクト
Proc.new {|a, b| a + b }
lambda {|a, b| a + b }
proc {|a, b| a + b }
->(a, b) { a + b }
どれも、callで引数を渡すと呼べる。
メソッドからProcオブジェクトへ
str = :to_s.to_proc
[1,2,3].method(:concat).to_proc.([4,5,6]) #=> [1,2,3,4,5,6]
def add(x, y) x + yend
method(:add).to_proc
Rubyでカリー化の説明
f = proc {|a, b| a + b }
f.call(1, 2) #=> 3
f.(1, 2) #=>3#callのシンタックスシュガー
---------------------------------------def f(a, b) a + bend
f(1, 2) #=> 3
Rubyでカリー化の説明(2)
g = proc {|a| proc {|b| a + b } }
h = g.(1) #=> Proch.(2) #=> 3
---------------------------------------def g(a) proc {|b| a + b }end
h = g(1).(2) #=> 3
関数の部分適用
実はさっきやったアレ
g = proc {|a| proc {|b| a + b } }
h = g.(1) #=> Proc ←これh.(2)
カリー化した関数に引数を渡して、関数作ること
それだけ
関数の部分適用(2)
でも関数型言語では大事例えばHaskellでは
ans = length $ filter (== 0) $ map (flip mod $ 3) $ map (+ 4) $ [1..100]
ansは1から100を、それぞれプラス4してそれぞれ3で割った余りにして、0だけ取り出して、できたリストの長さを返す
関数合成
数学の「関数合成」と同じイメージ
(f ◦ g)(x) = f(g(x))
プログラミングでは
aを引数に取りbが返り値の関数とbを引数に取りcが返り値の関数を
合成して
aを引数に取りcが返り値の関数を作る
(a → b) + (b → c) = (a → c)
まとめ
def plus(x, y) x + yend
f = method(:plus).to_proc.curry #カリー化
g = f.(3) #部分適用
h = :to_s
i = h >> g #関数合成
i.(5) #=>"8"
ちょっと補足(method(:plus).to_proc.curry.(3) >> :to_s).(5)
メソッドplusのシンボル:plusをオブジェクト化して、Methodオブジェクトにする。
そして、to_procでProcオブジェクトへ
proc {|x, y| x + y}
curryでカリー化して
proc {|x| proc {|y| x + y }}部分適用して proc {|y| 3 + yこれで関数合成できる!! }
おまけ:パイプライン演算子
F#にはパイプライン演算子がある。
a |> f |> g |> h |> i
fをgに適用してそれをhに適用してそれをiに適用する = i(h(g(f(a))))
rubyに似た感じのpipeメソッドを実装してみる。
こんな感じ
#1234を(*3 + 1)してsquareしてto_sして[:a, :b]とjoin
def square(x) x ** 2end
1234.pipe {|i| i * 3 + 1 } #1.pipe(&method(:square)) #2.pipe(&:to_s) #3.pipe(&:[:a, :b].method(:join)) #4 #=>"a13712209b"
Objectクラスにインクルードしてるのでどこでも使える。
カリー化、部分適用、関数合成と合わせてもOK