27
ruby_func

Ruby func org

Embed Size (px)

Citation preview

ruby_func

自己紹介

・Ruby歴半年ぐらいだけどあんまり使ってない・Haskell触った後にF#使い始めた・C#、F#がメインで、Rubyは時々

・ネタを挟まないと死んじゃう病

お品書き

・関数型言語って何?・第一級関数・カリー化・関数の部分適用・関数合成

関数型言語って何?

・すべてのものを関数(定数を含む) として表す・第一級関数:  引数が関数だったり、  返り値が関数だったり・一度宣言したものは再代入、

  変更不可 ・ループ処理なし

なるほどわからん

Rubyで考えてみよう

 今回はRubyで関数について考えてみる。

第一級関数

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

カリー化

複数の引数を取る関数↓

引数が「もとの関数の最初の引数」で戻り値が「もとの関数の残りの引数を取り、

結果を返す関数」にする

By Wikipedia

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

curryメソッド

複数の引数を取るProcオブジェクトをカリー化してくれる

proc {|x, y| x + y}

↓curry

proc {|x| proc {|y| x + y } }

Rubyでカリー化の説明(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)

関数合成(2)

関数合成(3)

f = :to_s

g = :upcase

h = f >> g

h.(:abc) #=>"ABC"

(:to_s >> :upcase).(:abc) #=> "ABC"

まとめ

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メソッドを実装してみる。

おまけ:パイプライン演算子(1)

こんな感じ

#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

本当に本当におわり