Upload
-
View
572
Download
0
Embed Size (px)
Citation preview
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話についてesm LT 2016/10/07
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
自己紹介▸ 久納 工▸ 2006 年入社▸ IT サービス事業部
Clojure▸ Clojure ( 発音は /'klouʒər/[2], クロージャー ) はプログラミング言語であり、 LISP 系の言語の方言の一つである。関数型プログラミングのプログラミングスタイルでのインタラクティブな開発を支援し、マルチスレッドプログラムの開発を容易化する汎用言語である。 Clojure 言語のプログラムは Java 仮想マシンと Microsoft .NET 共通言語ランタイムで動作する。 Clojure 言語は「データとしてのプログラムコード」 ( 英語 : 「 code as data 」 ) という思想で設計されており、洗練されたマクロ機構を持つ。
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
Wikipedia より
Clojure▸ Clojure ( 発音は /'klouʒər/[2], クロージャー ) はプログラミング言語であり、 LISP 系の言語の方言の一つである。関数型プログラミングのプログラミングスタイルでのインタラクティブな開発を支援し、マルチスレッドプログラムの開発を容易化する汎用言語である。 Clojure 言語のプログラムは Java 仮想マシンと Microsoft .NET 共通言語ランタイムで動作する。 Clojure 言語は「データとしてのプログラムコード」 ( 英語 : 「 code as data 」 ) という思想で設計されており、洗練されたマクロ機構を持つ。
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
Wikipedia より
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
スレッディングマクロとはClojure の S 式だと(reduce + 100 (map #(* %1 2) (filter even? [1 2 3 4])))
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
スレッディングマクロとはClojure の S 式だと(reduce + 100 (map #(* %1 2) (filter even? [1 2 3 4]))) こう実行されて
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
スレッディングマクロとはClojure の S 式だと(reduce + 100 (map #(* %1 2) (filter even? [1 2 3 4])));;=> 112
こうなる
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
スレッディングマクロとはスレッディングマクロだと(->> [1 2 3 4] (filter even?) (map #(* %1 2)) (reduce + 100))
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
スレッディングマクロとはスレッディングマクロだと(->> [1 2 3 4] (filter even?) (map #(* %1 2)) (reduce + 100))
こう実行されて
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
スレッディングマクロとはスレッディングマクロだと(->> [1 2 3 4] (filter even?) (map #(* %1 2)) (reduce + 100));;=> 112
こうなる
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
スレッディングマクロの利点スレッディングマクロ(->> [1 2 3 4] (filter even?) (map #(* %1 2)) (reduce + 100))
S 式のみ(reduce + 100 (map #(* %1 2) (filter even? [1 2 3 4])))
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
スレッディングマクロの利点スレッディングマクロ(->> [1 2 3 4] (filter even?) (map #(* %1 2)) (reduce + 100))
S 式のみ(reduce + 100 (map #(* %1 2) (filter even? [1 2 3 4])))
スレッディングマクロを使うと評価の時系列順に上から読むことが出来る
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
他にもあるスレッディングマクロの利点Clojure の S 式だと(reduce + 100 (map #(* %1 2) (filter even? [1 2 3 4])))
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
他にもあるスレッディングマクロの利点Clojure の S 式だと(reduce + 100..(map #(* %1 2)....(filter even?......[1 2 3 4])))
インデントはこうなる
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
他にもあるスレッディングマクロの利点スレッディングマクロだと(->> [1 2 3 4] (filter even?) (map #(* %1 2)) (reduce + 100))
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
他にもあるスレッディングマクロの利点スレッディングマクロだと(->>..[1 2 3 4]..(filter even?)..(map #(* %1 2))..(reduce + 100))
インデントはこうなる
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
他にもあるスレッディングマクロの利点スレッディングマクロ(->> [1 2 3 4] (filter even?) (map #(* %1 2)) (reduce + 100))
S 式のみ(reduce + 100 (map #(* %1 2) (filter even? [1 2 3 4])))
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
他にもあるスレッディングマクロの利点スレッディングマクロ(->>..[1 2 3 4]..(filter even?)..(map #(* %1 2))..(reduce + 100))
S 式のみ(reduce + 100..(map #(* %1 2)....(filter even?......[1 2 3 4])))
コードのインデントが深くならない
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
スレッディングマクロの利点
他にも有るけど割愛
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話についてesm LT 2016/10/07
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話についてesm LT 2016/10/07
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
第二部
esm LT 2016/10/07
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
Ruby のラムダ式で S 式相当の処理を書くと…
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
Ruby のラムダ式で S 式相当の処理を書くと…-> (collection) { collection.reduce(100, :+) }. call(-> (collection) { collection.map {|e| e * 2 } }. call(-> (collection) { collection.select(&:even?) }. call([1, 2, 3, 4])))
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
Ruby のラムダ式で S 式相当の処理を書くと…-> (collection) { collection.reduce(100, :+) }. call(-> (collection) { collection.map {|e| e * 2 } }. call(-> (collection) { collection.select(&:even?) }. call([1, 2, 3, 4])))
これをスレッディングマクロっぽく書けるようにする
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
基本のアイディア[ -> (collection) { collection.select(&:even?) }, -> (collection) { collection.map {|e| e * 2 } }, -> (collection) { collection.reduce(100, :+) }].reduce([1, 2, 3, 4]) { |acc, proc| proc.call(acc)}
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
基本のアイディア[ -> (collection) { collection.select(&:even?) }, -> (collection) { collection.map {|e| e * 2 } }, -> (collection) { collection.reduce(100, :+) }].reduce([1, 2, 3, 4]) { |acc, proc| proc.call(acc)}
Proc の配列に対して
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
基本のアイディア[ -> (collection) { collection.select(&:even?) }, -> (collection) { collection.map {|e| e * 2 } }, -> (collection) { collection.reduce(100, :+) }].reduce([1, 2, 3, 4]) { |acc, proc| proc.call(acc)}
Proc の配列に対して
reduce して、処理結果に順々に適用する
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
基本のアイディア[ -> (collection) { collection.select(&:even?) }, -> (collection) { collection.map {|e| e * 2 } }, -> (collection) { collection.reduce(100, :+) }].reduce([1, 2, 3, 4]) { |acc, proc| proc.call(acc)}=> 112
Proc の配列に対して
reduce して、処理結果に順々に適用する
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
とりあえずここまで実装してみるdef thread_last(*procs) procs[1..-1].reduce(procs.first) {|acc, proc| proc.call acc }end
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
とりあえずここまで実装してみるdef thread_last(*procs) procs[1..-1].reduce(procs.first) {|acc, proc| proc.call acc }end
thread_last( [1,2,3,4], -> (collection) { collection.select(&:even?) }, -> (collection) { collection.map {|e| e * 2 } }, -> (collection) { collection.reduce(100, :+) })
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
とりあえずここまで実装してみるdef thread_last(*procs) procs[1..-1].reduce(procs.first) {|acc, proc| proc.call acc }end
thread_last( [1,2,3,4], -> (collection) { collection.select(&:even?) }, -> (collection) { collection.map {|e| e * 2 } }, -> (collection) { collection.reduce(100, :+) })
=> 112
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
とりあえずここまで実装してみるdef thread_last(*procs) procs[1..-1].reduce(procs.first) {|acc, proc| proc.call acc }end
thread_last( [1,2,3,4], -> (collection) { collection.select(&:even?) }, -> (collection) { collection.map {|e| e * 2 } }, -> (collection) { collection.reduce(100, :+) })
=> 112 出来た!
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
基本のアイディア[ -> (collection) { collection.select(&:even?) }, -> (collection) { collection.map {|e| e * 2 } }, -> (collection) { collection.reduce(100, :+) }].reduce([1, 2, 3, 4]) { |acc, proc| proc.call(acc)}
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
こんなときはどうする?[ -> (collection) { collection.select(&:even?) }, -> (n, collection) { collection.map {|e| e * n } }, -> (collection) { collection.reduce(100, :+) }].reduce([1, 2, 3, 4]) { |acc, proc| proc.call(acc)}
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
こんなときはどうする?[ -> (collection) { collection.select(&:even?) }, -> (n, collection) { collection.map {|e| e * n } }, -> (collection) { collection.reduce(100, :+) }].reduce([1, 2, 3, 4]) { |acc, proc| proc.call(acc)} n を指定したい
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
こんなときはどうする?[ -> (collection) { collection.select(&:even?) }, -> (n, collection) { collection.map {|e| e * n } }, -> (collection) { collection.reduce(100, :+) }].reduce([1, 2, 3, 4]) { |acc, proc| proc.call(acc)}ArgumentError: wrong number of arguments (given 1, expected 2)
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
こんなときはどうする?[ -> (collection) { collection.select(&:even?) }, -> (n, collection) { collection.map {|e| e * n } }, -> (collection) { collection.reduce(100, :+) }].reduce([1, 2, 3, 4]) { |acc, proc| proc.call(acc)}ArgumentError: wrong number of arguments (given 1, expected 2)
一つの引数にだけProc を適用している
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
こんなときは[ -> (collection) { collection.select(&:even?) }, -> (n, collection) { collection.map {|e| e * n } }, -> (collection) { collection.reduce(100, :+) }].reduce([1, 2, 3, 4]) { |acc, proc| proc.call(acc)}
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
こんなときはこうする![ -> (collection) { collection.select(&:even?) }, -> (n, collection) { collection.map {|e| e * n } }.curry[2], -> (collection) { collection.reduce(100, :+) }].reduce([1, 2, 3, 4]) { |acc, proc| proc.call(acc)}
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
こんなときはこうする![ -> (collection) { collection.select(&:even?) }, -> (n, collection) { collection.map {|e| e * n } }.curry[2], -> (collection) { collection.reduce(100, :+) }].reduce([1, 2, 3, 4]) { |acc, proc| proc.call(acc)}=> 112
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
こんなときはこうする![ -> (collection) { collection.select(&:even?) }, -> (n, collection) { collection.map {|e| e * n } }.curry[2], -> (collection) { collection.reduce(100, :+) }].reduce([1, 2, 3, 4]) { |acc, proc| proc.call(acc)}
curry とは?
60sec で分かる ( と良いなと思う )カリー化と部分適用
60sec で分かる ( と良いなと思う ) カリー化と部分適用
カリー化とは関数がただ一つの引数だけをとるようにすること。この状態の関数をカリー化されていると呼ぶ。
2引数の関数はカリー化することで、引数の数が一つである関数と、その関数を返す引数の数が一つの関数に分けられる。
60sec で分かる ( と良いなと思う ) カリー化と部分適用
カリー化とはRuby で書くと…->(x, y) { x + y }.call(2, 3)=> 5
60sec で分かる ( と良いなと思う ) カリー化と部分適用
カリー化とはRuby で書くと…->(x, y) { x + y }.call(2, 3)=> 5
->(x) { -> (y) { x + y } }.call(2).call(3)=> 5
カリー化 ( 手動 )
60sec で分かる ( と良いなと思う ) カリー化と部分適用
カリー化とはRuby で書くと…->(x, y) { x + y }.call(2, 3)=> 5
->(x, y) { x + y }.curry[2][3]=> 5
カリー化
60sec で分かる ( と良いなと思う ) カリー化と部分適用
カリー化とはRuby で書くと…->(x, y) { x + y }.call(2, 3)
->(x, y) { x + y }.curry[2][3]
``-> (y) { 2 + y }`` 相当の Proc が返ってくる
カリー化
60sec で分かる ( と良いなと思う ) カリー化と部分適用
カリー化とは任意の数の引数の Proc をカリー化出来る
->(x, y, z) { x + y + z }.curry[2][3][4]=> 9->(x, y, z, xx, yy, zz) { x + y + z + xx + yy + zz }.curry[2][3][4][5][6][7]=> 27
60sec で分かる ( と良いなと思う ) カリー化と部分適用
カリー化とは途中で変数に取ることも出来る
partialized_proc = ->(x, y) { x + y }.curry[2]=> #<Proc:0x007feaa2f4c6f0 (lambda)>
partialized_proc[3]=> 5
60sec で分かる ( と良いなと思う ) カリー化と部分適用
カリー化とは途中で変数に取ることも出来る
partialized_proc = ->(x, y) { x + y }.curry[2]=> #<Proc:0x007feaa2f4c6f0 (lambda)>
partialized_proc[3]=> 5全ての引数が渡されると処理結果が返ってくる
60sec で分かる ( と良いなと思う ) カリー化と部分適用
部分適用とは部分適用とは、関数に対して全ての引数を一度に渡さず、一部の引数だけ渡すことができる仕組み。
60sec で分かる ( と良いなと思う ) カリー化と部分適用
部分適用とはpartialized_proc = ->(x, y) { x + y }.curry[2]=> #<Proc:0x007feaa2f4c6f0 (lambda)>partialized_proc[3]=> 5
60sec で分かる ( と良いなと思う ) カリー化と部分適用
部分適用とはpartialized_proc = ->(x, y) { x + y }.curry[2]=> #<Proc:0x007feaa2f4c6f0 (lambda)>partialized_proc[3]=> 5これが部分適用された状態の Proc
60sec で分かる ( と良いなと思う )カリー化と部分適用
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
⚠ 注意事項
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
⚠ 注意事項Clojure ではスレッディングマクロの実現にTransducers という機構を使用しています。
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
⚠ 注意事項Clojure ではスレッディングマクロの実現にTransducers という機構を使用しています。
が、ここでは Transducers については触れません。
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
この書き方を[ -> (collection) { collection.select(&:even?) }, -> (n, collection) { collection.map {|e| e * n } }.curry[2], -> (collection) { collection.reduce(100, :+) }].reduce([1, 2, 3, 4]) { |acc, proc| proc.call(acc)}
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
thread_last に適用すると…thread_last( [1,2,3,4], -> (collection) { collection.select(&:even?) }, -> (n, collection) { collection.map {|e| e * n }.curry[2] }, -> (collection) { collection.reduce(100, :+) })
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
thread_last に適用すると…thread_last( [1,2,3,4], -> (collection) { collection.select(&:even?) }, -> (n, collection) { collection.map {|e| e * n }.curry[2] }, -> (collection) { collection.reduce(100, :+) })=> 112
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
thread_last に適用すると…thread_last( [1,2,3,4], -> (collection) { collection.select(&:even?) }, -> (n, collection) { collection.map {|e| e * n }.curry[2] }, -> (collection) { collection.reduce(100, :+) })=> 112 利用者が curry しないといけない
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
いちいち curry したくない……
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
いちいち curry したくない……class Proc def |(arg) self.curry[arg] endend
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
いちいち curry したくない……class Proc def |(arg) self.curry[arg] endend
Proc に | メソッドを生やす
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
いちいち curry したくない……class Proc def |(arg) self.curry[arg] endend
thread_last( [1,2,3,4], -> (collection) { collection.select(&:even?) }, -> (n, collection) { collection.map {|e| e * n } } | 2, -> (collection) { collection.reduce(100, :+) })
Proc に | メソッドを生やす
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
いちいち curry したくない……class Proc def |(arg) self.curry[arg] endend
thread_last( [1,2,3,4], -> (collection) { collection.select(&:even?) }, -> (n, collection) { collection.map {|e| e * n } } | 2, -> (collection) { collection.reduce(100, :+) })
=> 112
Proc に | メソッドを生やす
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
thread_last(->>) が出来た!
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
thread_last(->>) が出来た!
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
色々なスレッディングマクロ‣ ->‣ ->>‣ as->‣ some->‣ some->>‣ cond->‣ cond->>
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
色々なスレッディングマクロ‣ ->‣ ->>‣ as->‣ some->‣ some->>‣ cond->‣ cond->>
まだまだ戦いは続く……
▸ 一番最初の値に Proc が来ることもある。ので、最初が Proc ならば実行する。もちろん引数の数が不定なのでcall ではなく curry で。
▸ Proc に生やした | メソッドで即時 curry していたけど、 thread_first(->) のときは実行する寸前までカリー化したくないので、実行時まで配列で持っているようにした。▸ thread_as(as->) するときは引数の挿入場所を選べる。シンボルで表現したいが Proc の結果がシンボルになることも有るので、表現しきれない。ので Proc に新たなメソッド & を生やした。かつ、挿入位置を表す型を用意した。▸ map とか reduce とかするラムダ式を一々書きたくない……ので、引数で受け取った Collection をレシーバーとして map を実行するメソッド _map(proc) が include したクラスの self に生えるようにした。 reduce,
select に関しても同様。▸ ↑ で作ったメソッドで、 Proc を渡したいときと Block を渡したいときがある。
▸ Clojure の ->> と同じようなメソッドのエイリアスを設けたいが、 - 始まりのメソッドは書けない。マルチバイトはいけるので、色々考えた結果これが一番近いと思う。 `` ー✈✈ ``
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
戦いの記録 ( ダイジェスト版 )
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
色々やった結果-> (collection) { collection.reduce(100, :+) }. call(-> (collection) { collection.map {|e| e * 2 } }. call(-> (collection) { collection.select(&:even?) }. call([1, 2, 3, 4])))
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
色々やった結果-> (collection) { collection.reduce(100, :+) }. call(-> (collection) { collection.map {|e| e * 2 } }. call(-> (collection) { collection.select(&:even?) }. call([1, 2, 3, 4])))
thread_last( [1, 2, 3, 4], _select(&:even?), _map(-> (e) { e * 2 }), _reduce(100, &:+))
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
色々やった結果-> (collection) { collection.reduce(100, :+) }. call(-> (collection) { collection.map {|e| e * 2 } }. call(-> (collection) { collection.select(&:even?) }. call([1, 2, 3, 4])))
thread_last( [1, 2, 3, 4], _select(&:even?), _map(-> (e) { e * 2 }), _reduce(100, &:+)) ここまで短く簡潔に書ける!!!
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
なお……
thread_last( [1, 2, 3, 4], _select(&:even?), _map(-> (e) { e * 2 }), _reduce(100, &:+))
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
なお……
thread_last( [1, 2, 3, 4], _select(&:even?), _map(-> (e) { e * 2 }), _reduce(100, &:+))
このコードをラムダ式を使わずに普通に書くと……
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
なお……[1, 2, 3, 4]. select(&:even?). map {|e| e * 2}. reduce(100, :+)
thread_last( [1, 2, 3, 4], _select(&:even?), _map(-> (e) { e * 2 }), _reduce(100, &:+))
このコードをラムダ式を使わずに普通に書くと……
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
結論。
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
結論。
素の Ruby すごい
Clojure のスレッディングマクロっぽいものを Ruby で実装してみた話について
第二部
esm LT 2016/10/07