Successfully reported this slideshow.

怠惰なRubyistへの道 fukuoka rubykaigi01

726 views

Published on

福岡Ruby会議01 で発表したスライドを公開用にPDF に変換したものです。アニメーションやデモ動画はスライドに置き換えています。

Published in: Technology
  • Be the first to comment

怠惰なRubyistへの道 fukuoka rubykaigi01

  1. 1. 怠惰なRubyistへの道Enumerator::Lazyの使いかた Chikanaga Tomoyuki (@nagachika) Fukuoka.rb 2012.12.1 Fukuoka Ruby Kaigi 01
  2. 2. AgendaEnumerableEnumeratorEnumerator::Lazy
  3. 3. AgendaEnumerable2.0 の新機能(NEW)EnumeratorEnumerator::Lazy
  4. 4. ATTENTION 今日の話は Ruby 2.0 の新機能について
  5. 5. Ruby 2.02013年リリース予定[ruby-dev:44691] よりAug. 24 2012: big-feature freezeOct. 24 2012: feature freezeFeb. 24 2013: 2.0 releaseRuby の生誕 20 周年
  6. 6. Ruby 2.0Aug. 24 2012: big-feature freezeOct. 24 2012: feature freezeNov. 2 2012: 2.0 preview1 released←今ココDec. 1 2012: 2.0 preview2 releasedFeb. 24 2013: 2.0 release
  7. 7. 2.0 is in development ※注※ 仕様は開発中のものでありリリース版では変更される場合があります
  8. 8. Install Ruby 2.02.0.0 で未来を垣間見るrvm, rbenv を使っていればrvm install ruby-headrbenv install 2.0-devel
  9. 9. Self Introductiontwitter id: @nagachikaruby trunk changes(http://d.hatena.ne.jp/nagachika)CRuby committerYokohama.rb → Fukuoka.rbSound.rb(ruby-coreaudio, ruby-puredata)
  10. 10. Recent ActivityRDoc 3.9.4 → 4.0.0RubyGems 1.8.24 → 2.0Rake 0.9.2.2 → 0.9.5 (Rake 10?)MiniTest 3.4.0 → 4.3.2空前のメジャーバージョンアップラッシュ
  11. 11. Recent Activityかけこみコミットラッシュ
  12. 12. New Featuresキーワード引数 Array#bsearchModule#prepend GC bitmap markingRefinement Debugger/ProfilerEnumerator::Lazy API スクリプトエンコーディングDTrace対応 UTF-8化TracePoint String#b
  13. 13. Keyword Argumentsdef tupple(key: a , value: b ) { key => value }endtupple key: k , value: v # =>{ k => v }tupple value: 42 # => { a => 42 }tupple # => { a => b }
  14. 14. Module#prepend obj C M Objectclass C; include M; endModule#include (Mixin) は定義済みのメソッドを上書きできない obj M C Objectclass C; prepend M; endModule#prepend は C のメソッドを上書きできるAround Alias (alias_method_chain) のかわりに使える
  15. 15. Module#prependmodule Title def initialize(title, name) @title = title super(name) end def name @title + super endendclass Person def initialize(name) @name=name end def name @name end prepend TitleendPerson.new( Mr. , nagachika ).name #=> Mr.nagachika
  16. 16. Module#prependprepend なしだと(include だと)class Person ...endmodule Title ...endclass Men < Person include Title def initialize super; @title = Mr. endend
  17. 17. Refinement大幅路線変更?
  18. 18. @a_matsuda++
  19. 19. Enumerator::Lazy
  20. 20. Enumerator::Lazy の前に
  21. 21. Enumerable
  22. 22. Enumerableもちろん使ってますよね?
  23. 23. Array, Hash, IO...Array や Hash, IO は Enumerable を include している
  24. 24. Enumerable【形容詞】 数えられる [日本語 WordNet(英和)) 可算;可付番 [クロスランゲージ 37分野専門語辞書]Capable of being enumerated;countable [Wikitionary]
  25. 25. Enumerable「each メソッドを呼ぶと、何かが順に(N回) yield で渡される」というルール(N >= 0)
  26. 26. Enumerableeach メソッドが定義されているクラスに include して使う(Mix-in)
  27. 27. Enumerableeach だけ用意しておけばmap, select, reject, grep, etc...といったメソッドが使えるようになる(これらのメソッドが each を使って実装されている)
  28. 28. An Exampleclass A include Enumerable def each yield 0 yield 1 yield 2 endendA.new.map{|i| i * 2 } # => [ 0, 2, 4 ]
  29. 29. ここまでのまとめEnumerable は each メソッドを使って多彩なメソッドを追加するなにが「数え上げられる」のかはeach メソッドがどのようにyield を呼び出すかで決まる
  30. 30. EnumeratorEnumerator使ってます?
  31. 31. Enumeratoreach に渡すブロックを保留した状態でオブジェクトとして持ち回る
  32. 32. to_enum, enum_forEnumerator の作りかた• Enumerator.new(obj, method=:each, *args)• obj.to_enum(method=:each, *args)• obj.enum_for(method=:each, *args)• Enumerator.new{|y| ...} ↑これだけちょっと特殊
  33. 33. Object#enum_foreach 以外の任意のメソッドの呼び出しをEnumerator にできる(実は Enumerable でなくてもいい)e_byte = $stdin.enum_for(:each_byte) => バイトごとに yielde_line = $stdin.enum_for(:each_line) => 行ごとに yield
  34. 34. Enumerator.new{|y|}e = Enumerator.new{|y| y << 0 y << :foo y << 2}ブロックが each の代わり。ブロックパラメータ y に << するとyield することになる。
  35. 35. Enumerator.new{|y|}class A def each yield 0 yield :bar yield 2 endendA.new.to_enumと同じ
  36. 36. Enumerator as External Iterator外部イテレータとして使えるe = (0..2).to_enume.next # => 0 each が yield する値がe.next # => 1 順に next で取り出せるe.next # => 2e.next=> StopIteration: iteration reached an end
  37. 37. Method Chainmap, select など一部のメソッドはブロックを省略すると Enumerator を返すので複数のメソッドを組み合わせて使うary.map.with_index { |v,i| ... }
  38. 38. Method Chain[:a, :b, :c, :d].map.with_index do |sym, i| i.even? ? sym : iend=> [:a, 1, :c, 3] Enumerator 独自のメソッド
  39. 39. with_index[ruby-dev:31953] Matz wrote... mapがenumeratorを返すとobj.map.with_index{|x,i|....}ができるのが嬉しいので導入したのでした。というわけで、* eachにもmapにもwith_indexが付けられて嬉しいというのが本当の理由です。
  40. 40. Secret Story of Enumerator. Enumerator はwith_index の為に 生まれた!
  41. 41. ここまでのまとめ• Enumerator • 外部イテレータ化 • メソッドチェーン•
  42. 42. Enumerator::Lazy2.0 の新機能
  43. 43. Enumerator::LazyEnumeratorのサブクラス
  44. 44. Enumerable#lazyEnumerator::Lazy の作りかたEnumerable#lazy を呼ぶ>> (0..5).lazy=> #<Enumerator::Lazy: 0..5>
  45. 45. Enumerator::LazyLazyのmap,select,zipなど一部のメソッドがさらにLazyを返す>> (0..5).lazy.map{|i| i * 2}=> #<Enumerator::Lazy:#<Enumerator::Lazy: 0..5>:map>
  46. 46. Enumerator::Lazy> Enumerator::Lazy.instance_methods(false) map collect flat_mapcollect_concat select find_all reject grep zip take take_while drop drop_wihle cycle lazy force [ruby 2.0.0dev (2012-05-30 trunk 35844)]
  47. 47. forceEnumrator::Lazy#forceto_a の alias遅延されているイテレータの評価を実行
  48. 48. A Lazy Demo>> l = (0..5).lazy=> <Enumerator::Lazy: 0..5>>> l_map = l.map{|i| p i; i * 2 }=> <Enumerator::Lazy: #<Enumerator::Lazy: 0..5>:map># この時点ではまだブロックの内容は実行されていない!>> l_map.force012345=> [0, 2, 4, 6, 8, 10]# force を呼ぶと実行される
  49. 49. Enumeratorとの違いMethod Chain の使いかたがより強力に中間データの抑制
  50. 50. Pitfall of Enumerator (1)map, select のようにブロックの戻り値を利用するメソッドを複数繋げることができない(しても意味がなくなる)(0..5).map.select{|i| i % 2 == 0 } => [ 0, 2, 4 ]↑ map の意味がなくなっている
  51. 51. True Method Chainmap や select といったメソッドもいくつでもMethod Chain できる。そう、Lazy ならね。# 1 から 1000 のうち任意の桁に7を含む数を返す(1..1000).lazy.map(&:to_s).select{|s| /7/ =~ s}.force
  52. 52. Pitfall of Enumerator (2)map, select のようにブロックの戻り値を利用するメソッドを外部イテレータ化しても意味がないe = (0..5).mape.next # => 0e.next # => 1e.next # => 2e.next # => 3↑ map の意味がなくなっている
  53. 53. True External Iteratormap などにブロックを渡せるので適用した結果を順に取り出せるl = (0..5).lazy.select{|i| i.even? }l.next # => 0l.next # => 2l.next # => 4
  54. 54. Efficient Memory UsageLazy は yield 毎に Method Chain の最後まで処理される
  55. 55. Enumerable versionlarge_array.map{...}.select{...}
  56. 56. Enumerable versionlarge_array.map{...}.select{...} map
  57. 57. Enumerable versionlarge_array.map{...}.select{...} map select
  58. 58. Enumerable versionlarge_array.map{...}.select{...} GCされる map select
  59. 59. Enumerable versionlarge_array.map{...}.select{...} GCされる map select map 結果のためlarge_array と同じくらいの 中間データが必要
  60. 60. Lazy versionlarge_array.lazy.map{...}.select{...}.force
  61. 61. Lazy versionlarge_array.lazy.map{...}.select{...}.force
  62. 62. Lazy versionlarge_array.lazy.map{...}.select{...}.force GC ed
  63. 63. Lazy versionlarge_array.lazy.map{...}.select{...}.force
  64. 64. Lazy versionlarge_array.lazy.map{...}.select{...}.force GC ed
  65. 65. Lazy versionlarge_array.lazy.map{...}.select{...}.force
  66. 66. Lazy versionlarge_array.lazy.map{...}.select{...}.force 中間データが不要
  67. 67. Lazy Chain Demo>> (0..4).lazy.select{|i| p “select:#{i}”; i.event? }.map{|i| p “map:#{i}”; i * 2 }.force“select:0”“map:0”“select:1”“select:2”“map:2”“select:3”“select:4”“map:4”=> [0, 4, 8]
  68. 68. Lazy with IO例) $stdin から空行を読むまでの各行の文字数の配列を返す$stdin.lazy.take_while{|l| ! l.chomp.empty?}.map {|l| l.chomp.size }
  69. 69. Lazy with IOEnumerable, Enumerator だと空行まで先に読んでしまう。Lazy なら1行読むたびに take_while/mapのブロックが実行される → 逐次処理できる
  70. 70. Lazy List無限に続く要素を扱えるlist = (0..Float::INFINITY)list.map{|i| i * 2}.take(5) => 返ってこないlist.lazy.map{|i| i * 2 }.take(5).force => [0, 2, 4, 6, 8]
  71. 71. Interactivity実行順序を利用してインタラクティブに(0..Float::INFINITY).lazy.map{|i| p num line = $stdin.gets line.chomp if line}.take_while{|l| l and not l.empty?}.force
  72. 72. Pitfall of Lazy?メモ化されない(force を2回呼ぶともう一度 each が呼ばれる) IOなど副作用のある each だと結果が 変化する ex) $stdin.lazy.take(1)enum_for のように each のかわりになるメソッドを指定できない $stdin.enum_for(:each_byte).lazy $stdin.lazy(:each_byte) と書きたい
  73. 73. Lazy as a Better Enumerator Lazy は Enumerator の 正統進化版
  74. 74. Happy LazyProgramming!

×