Skip to content

Instantly share code, notes, and snippets.

@otofu-square
Last active November 30, 2018 08:04
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save otofu-square/30da7ac415b5c42a39f8922cf44c751e to your computer and use it in GitHub Desktop.
Save otofu-square/30da7ac415b5c42a39f8922cf44c751e to your computer and use it in GitHub Desktop.
Implement compose function in Ruby
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."
@sakuro
Copy link

sakuro commented Jul 14, 2017

composeProc#* として定義すると見易くなりそう。そして yuroyoro/lambda_driver へ。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment