Upgrade to Pro — share decks privately, control downloads, hide ads and more …

esm lt Clojure like threading macro

kunou
October 11, 2016

esm lt Clojure like threading macro

kunou

October 11, 2016
Tweet

More Decks by kunou

Other Decks in Technology

Transcript

  1. Clojure ▸ Clojure (ൃԻ͸/'klouʒər/[2], Ϋϩʔδϟʔ)͸ϓϩάϥϛϯάݴ ޠͰ͋ΓɺLISPܥͷݴޠͷํݴͷҰͭͰ͋Δɻؔ਺ܕϓϩάϥ ϛϯάͷϓϩάϥϛϯάελΠϧͰͷΠϯλϥΫςΟϒͳ։ൃ Λࢧԉ͠ɺϚϧνεϨουϓϩάϥϜͷ։ൃΛ༰қԽ͢Δ൚༻ ݴޠͰ͋ΔɻClojureݴޠͷϓϩάϥϜ͸JavaԾ૝Ϛγϯͱ Microsoft

    .NET ڞ௨ݴޠϥϯλΠϜͰಈ࡞͢ΔɻClojureݴޠ͸ ʮσʔλͱͯ͠ͷϓϩάϥϜίʔυʯ (ӳޠ:ʮcode as dataʯ) ͱ͍͏ࢥ૝Ͱઃܭ͞Ε͓ͯΓɺચ࿅͞ΕͨϚΫϩػߏΛ࣋ͭɻ ClojureͷεϨοσΟϯάϚΫϩͬΆ͍΋ͷΛRubyͰ࣮૷ͯ͠Έͨ࿩ʹ͍ͭͯ WikipediaΑΓ
  2. Clojure ▸ Clojure (ൃԻ͸/'klouʒər/[2], Ϋϩʔδϟʔ)͸ϓϩάϥϛϯάݴ ޠͰ͋ΓɺLISPܥͷݴޠͷํݴͷҰͭͰ͋Δɻؔ਺ܕϓϩάϥ ϛϯάͷϓϩάϥϛϯάελΠϧͰͷΠϯλϥΫςΟϒͳ։ൃ Λࢧԉ͠ɺϚϧνεϨουϓϩάϥϜͷ։ൃΛ༰қԽ͢Δ൚༻ ݴޠͰ͋ΔɻClojureݴޠͷϓϩάϥϜ͸JavaԾ૝Ϛγϯͱ Microsoft

    .NET ڞ௨ݴޠϥϯλΠϜͰಈ࡞͢ΔɻClojureݴޠ͸ ʮσʔλͱͯ͠ͷϓϩάϥϜίʔυʯ (ӳޠ:ʮcode as dataʯ) ͱ͍͏ࢥ૝Ͱઃܭ͞Ε͓ͯΓɺચ࿅͞ΕͨϚΫϩػߏΛ࣋ͭɻ ClojureͷεϨοσΟϯάϚΫϩͬΆ͍΋ͷΛRubyͰ࣮૷ͯ͠Έͨ࿩ʹ͍ͭͯ WikipediaΑΓ
  3. 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]))) スレッディングマクロを使うと 評価の時系列順に上から読むことが出来る
  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]))) コードのインデントが深くならない
  5. 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]))) これをスレッディングマクロっぽく 書けるようにする
  6. 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)
 }
  7. 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の配列に対して
  8. 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して、処理結果に順々に適用する
  9. 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して、処理結果に順々に適用する
  10. 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, :+) }
 )
  11. 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
  12. 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 出来た!
  13. 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)
 }
  14. 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)
 }
  15. 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を指定したい
  16. 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)
  17. 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を適用している
  18. 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)
 }
  19. 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)
 }
  20. 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
  21. 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とは?
  22. 60sec Ͱ෼͔Δ(ͱྑ͍ͳͱࢥ͏)ΧϦʔԽͱ෦෼ద༻ ΧϦʔԽͱ͸ Ruby で書くと… ->(x, y) { x +

    y }.call(2, 3) => 5 ->(x) { -> (y) { x + y } }.call(2).call(3) => 5 カリー化(手動)
  23. 60sec Ͱ෼͔Δ(ͱྑ͍ͳͱࢥ͏)ΧϦʔԽͱ෦෼ద༻ ΧϦʔԽͱ͸ Ruby で書くと… ->(x, y) { x +

    y }.call(2, 3) => 5 ->(x, y) { x + y }.curry[2][3] => 5 カリー化
  24. 60sec Ͱ෼͔Δ(ͱྑ͍ͳͱࢥ͏)ΧϦʔԽͱ෦෼ద༻ ΧϦʔԽͱ͸ Ruby で書くと… ->(x, y) { x +

    y }.call(2, 3) ->(x, y) { x + y }.curry[2][3] ``-> (y) { 2 + y }`` 相当のProcが返ってくる カリー化
  25. 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
  26. 60sec Ͱ෼͔Δ(ͱྑ͍ͳͱࢥ͏)ΧϦʔԽͱ෦෼ద༻ ΧϦʔԽͱ͸ 途中で変数に取ることも出来る partialized_proc = ->(x, y) { x

    + y }.curry[2] => #<Proc:0x007feaa2f4c6f0 (lambda)> partialized_proc[3] => 5 全ての引数が渡されると処理結果が返ってくる
  27. 60sec Ͱ෼͔Δ(ͱྑ͍ͳͱࢥ͏)ΧϦʔԽͱ෦෼ద༻ ෦෼ద༻ͱ͸ partialized_proc = ->(x, y) { x +

    y }.curry[2] => #<Proc:0x007feaa2f4c6f0 (lambda)> partialized_proc[3] => 5
  28. 60sec Ͱ෼͔Δ(ͱྑ͍ͳͱࢥ͏)ΧϦʔԽͱ෦෼ద༻ ෦෼ద༻ͱ͸ partialized_proc = ->(x, y) { x +

    y }.curry[2] => #<Proc:0x007feaa2f4c6f0 (lambda)> partialized_proc[3] => 5 これが部分適用された状態のProc
  29. 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)
 }
  30. 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
  31. 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しないといけない
  32. ClojureͷεϨοσΟϯάϚΫϩͬΆ͍΋ͷΛRubyͰ࣮૷ͯ͠Έͨ࿩ʹ͍ͭͯ ͍͍ͪͪcurryͨ͘͠ͳ͍…… class Proc
 def |(arg)
 self.curry[arg]
 end
 end thread_last(


    [1,2,3,4],
 -> (collection) { collection.select(&:even?) },
 -> (n, collection) { collection.map {|e| e * n } } | 2,
 -> (collection) { collection.reduce(100, :+) }
 ) Procに | メソッドを生やす
  33. ClojureͷεϨοσΟϯάϚΫϩͬΆ͍΋ͷΛRubyͰ࣮૷ͯ͠Έͨ࿩ʹ͍ͭͯ ͍͍ͪͪcurryͨ͘͠ͳ͍…… class Proc
 def |(arg)
 self.curry[arg]
 end
 end thread_last(


    [1,2,3,4],
 -> (collection) { collection.select(&:even?) },
 -> (n, collection) { collection.map {|e| e * n } } | 2,
 -> (collection) { collection.reduce(100, :+) }
 ) => 112 Procに | メソッドを生やす
  34. ▸ Ұ൪࠷ॳͷ஋ʹ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Ͱ࣮૷ͯ͠Έͨ࿩ʹ͍ͭͯ 戦いの記録(ダイジェスト版)
  35. ClojureͷεϨοσΟϯάϚΫϩͬΆ͍΋ͷΛRubyͰ࣮૷ͯ͠Έͨ࿩ʹ͍ͭͯ ৭ʑ΍ͬͨ݁Ռ -> (collection) { collection.reduce(100, :+) }.
 call(-> (collection)

    { collection.map {|e| e * 2 } }.
 call(-> (collection) { collection.select(&:even?) }.
 call([1, 2, 3, 4]))) 
 
 
 

  36. 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, &:+)
 )
  37. 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, &:+)
 ) こ こ まで 短 く簡 潔 に 書 け る! ! !
  38. ClojureͷεϨοσΟϯάϚΫϩͬΆ͍΋ͷΛRubyͰ࣮૷ͯ͠Έͨ࿩ʹ͍ͭͯ ͳ͓…… 
 
 thread_last(
 [1, 2, 3, 4],
 _select(&:even?),


    _map(-> (e) { e * 2 }),
 _reduce(100, &:+)
 ) このコードをラムダ式を使わずに 普通に書くと……
  39. 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, &:+)
 ) このコードをラムダ式を使わずに 普通に書くと……