Last active
November 30, 2018 08:04
-
-
Save otofu-square/30da7ac415b5c42a39f8922cf44c751e to your computer and use it in GitHub Desktop.
Implement compose function in Ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'pp' | |
# block/proc/lambda のおさらい① | |
# | |
# - block とは | |
# メソッドに「処理」を渡したい時に使うもの | |
def hoge(&block) | |
block("World") | |
end | |
hoge do |str| | |
puts "Hello ${str}" | |
end | |
# => "Hello World" | |
# hoge はこうやっても書けるよ | |
def hoge | |
yield("World") | |
end | |
# block/proc/lambda のおさらい② | |
# | |
# - Proc オブジェクトとは | |
# ブロックは変数化出来ない | |
# ならオブジェクトにしちゃえばいいよね -> Proc オブジェクト | |
puts_hello = Proc.new do |str| | |
puts "Hello #{str}" | |
end | |
def hoge(proc) | |
proc.call('World') | |
proc.('World') | |
proc['World'] | |
end | |
hoge(puts_hello) | |
# block/proc/lambda のおさらい③ | |
# | |
# - lambda とは | |
# Proc インスタンスを簡単に作るための記法 | |
# Proc.new と lambda の細かい違いはある (参考: https://qa.atmarkit.co.jp/q/68) | |
puts_hello = -> (str) { puts "Hello #{str}" } | |
def hoge(lambda) | |
lambda.call('World') | |
lambda.('World') | |
lambda['World'] | |
end | |
hoge(puts_hello) | |
# - 関数合成とは | |
# f(x), g(x) という 2 つの関数があった時に | |
# h(x) => f(g(x)) という結果を返す関数 h(x) を作り出すこと | |
# | |
# 例として加算の関数、除算の関数を合成して新しい関数を作ってみる | |
# 加算のラムダ(Procオブジェクト) | |
add = -> (x, y) { x + y }.curry | |
add_by_2 = add[2] # カリー化すると簡単に部分適用できる | |
# => add_by_2 = (2, y) => { 2 + y } | |
# 除算のラムダ | |
divide = -> (x, y) { y / x }.curry | |
divide_by_3 = divide[3] | |
# => divide_by_3 = (3, y) => { 3 / y } | |
# 受け取った引数に 2 を足して 3 で割る関数を生成してみるよ | |
add_by_2_and_divide_by_3 = | |
-> (x) { divide_by_3[add_by_2[x]] } | |
add_by_2_and_divide_by_3[4] | |
# => 2 | |
# 関数合成を一般化してメソッドにしてみましょう | |
# | |
# 2つの関数を合成するメソッド | |
def compose(f, g) | |
-> (x) { f[g[x]] } | |
end | |
# 3つ以上の関数を合成するメソッド | |
# 後 -> 前の順に関数を合成する | |
def compose(*funcs) | |
funcs.reduce do |f, g| | |
-> (x) { f[g[x]] } | |
end | |
end | |
# さっきの add_by_2_and_divide_by_3 はこう書けるよ | |
add_by_2_and_divide_by_3 = | |
compose( | |
divide_by_3, | |
add_by_2 | |
) | |
# 関数合成って結局何が便利なの? | |
# -> 複数のシンプルな関数を組み合わせて様々な複雑な処理を作り出せる | |
# | |
# 例として文字列にする関数、ログに出力する文字列を作り出す関数を作って | |
# 計算結果をログ情報として出力する関数を合成して作ってみる | |
# 受け取った引数を文字列に変換する関数 | |
stringify = -> (val) { val.to_s } | |
# ログ用に出力を整形する関数 | |
to_log_message = -> (val) { "The answer is #{val}." } | |
# Let's 合成 | |
calculate_and_output = | |
compose( | |
to_log_message, | |
stringify, | |
divide_by_3, | |
add_by_2 | |
) | |
pp calculate_and_output[10] | |
# => "The answer is 4." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
compose
をProc#*
として定義すると見易くなりそう。そして yuroyoro/lambda_driver へ。