怠惰なrubyistへの道 fukuoka rubykaigi01

74
怠惰なRubyistへの道 Enumerator::Lazyの使いかた Chikanaga Tomoyuki (@nagachika) Fukuoka.rb 2012.12.1 Fukuoka Ruby Kaigi 01

Upload: nagachika-t

Post on 18-Nov-2014

735 views

Category:

Technology


1 download

DESCRIPTION

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

TRANSCRIPT

Page 1: 怠惰なRubyistへの道 fukuoka rubykaigi01

怠惰なRubyistへの道 Enumerator::Lazyの使いかた

Chikanaga Tomoyuki(@nagachika)Fukuoka.rb

2012.12.1 Fukuoka Ruby Kaigi 01

Page 2: 怠惰なRubyistへの道 fukuoka rubykaigi01

Agenda

Enumerable

Enumerator

Enumerator::Lazy

Page 3: 怠惰なRubyistへの道 fukuoka rubykaigi01

Agenda

Enumerable

2.0 の新機能(NEW)

Enumerator

Enumerator::Lazy

Page 4: 怠惰なRubyistへの道 fukuoka rubykaigi01

ATTENTION

今日の話はRuby 2.0 の

新機能について

Page 5: 怠惰なRubyistへの道 fukuoka rubykaigi01

Ruby 2.0

2013年リリース予定[ruby-dev:44691] より

Aug. 24 2012: big-feature freeze

Oct. 24 2012: feature freeze

Feb. 24 2013: 2.0 release

Ruby の生誕 20 周年

Page 6: 怠惰なRubyistへの道 fukuoka rubykaigi01

Ruby 2.0

Aug. 24 2012: big-feature freeze

Oct. 24 2012: feature freeze

Nov. 2 2012: 2.0 preview1 released

←今ココ

Dec. 1 2012: 2.0 preview2 released

Feb. 24 2013: 2.0 release

Page 7: 怠惰なRubyistへの道 fukuoka rubykaigi01

2.0 is in development

※注※仕様は開発中のものであり

リリース版では変更される場合があります

Page 8: 怠惰なRubyistへの道 fukuoka rubykaigi01

Install Ruby 2.0

2.0.0 で未来を垣間見る

rvm, rbenv を使っていれば

rvm install ruby-head

rbenv install 2.0-devel

Page 9: 怠惰なRubyistへの道 fukuoka rubykaigi01

Self Introduction

twitter id: @nagachika

ruby trunk changes(http://d.hatena.ne.jp/nagachika)

CRuby committer

Yokohama.rb → Fukuoka.rb

Sound.rb(ruby-coreaudio, ruby-puredata)

Page 10: 怠惰なRubyistへの道 fukuoka rubykaigi01

Recent Activity

RDoc 3.9.4 → 4.0.0

RubyGems 1.8.24 → 2.0

Rake 0.9.2.2 → 0.9.5 (Rake 10?)

MiniTest 3.4.0 → 4.3.2

空前のメジャーバージョンアップラッシュ

Page 11: 怠惰なRubyistへの道 fukuoka rubykaigi01

Recent Activity

かけこみコミットラッシュ

Page 12: 怠惰なRubyistへの道 fukuoka rubykaigi01

New Features

キーワード引数

Module#prepend

Refinement

Enumerator::Lazy

DTrace対応

TracePoint

Array#bsearch

GC bitmap marking

Debugger/Profiler APIスクリプトエンコーディング UTF-8化

String#b

Page 13: 怠惰なRubyistへの道 fukuoka rubykaigi01

Keyword Arguments

def tupple(key: “a”, value: “b”) { key => value }end

tupple key: “k”, value: “v”# => {“k”=>”v”}

tupple value: 42 # => { “a” => 42 }

tupple # => {“a”=> “b”}

Page 14: 怠惰なRubyistへの道 fukuoka rubykaigi01

Module#prepend

class C; include M; endModule#include (Mixin) は定義済みのメソッドを上書きできない

C M Objectobj

class C; prepend M; endModule#prepend は C のメソッドを上書きできるAround Alias (alias_method_chain) のかわりに使える

CM Objectobj

Page 15: 怠惰なRubyistへの道 fukuoka rubykaigi01

Module#prepend

module 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”

Page 16: 怠惰なRubyistへの道 fukuoka rubykaigi01

Module#prepend

prepend なしだと(include だと)

class Person ...end

module Title ...end

class Men < Person include Title def initialize super; @title = “Mr.” endend

Page 17: 怠惰なRubyistへの道 fukuoka rubykaigi01

Refinement

大幅路線変更?

Page 18: 怠惰なRubyistへの道 fukuoka rubykaigi01

@a_matsuda++

Page 19: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerator::Lazy

Page 20: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerator::Lazy

の前に

Page 21: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerable

Page 22: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerable

もちろん使ってますよね?

Page 23: 怠惰なRubyistへの道 fukuoka rubykaigi01

Array, Hash, IO...

Array や Hash, IO は Enumerable を

include している

Page 24: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerable

【形容詞】 数えられる [日本語 WordNet(英和))

可算;可付番 [クロスランゲージ 37分野専門語辞書]

Capable of being enumerated; countable [Wikitionary]

Page 25: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerable

「each メソッドを呼ぶと、何かが順に(N回) yield で渡される」というルール(N >= 0)

Page 26: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerable

each メソッドが定義されているクラスに include して使う(Mix-in)

Page 27: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerable

each だけ用意しておけばmap, select, reject, grep, etc...といったメソッドが使えるようになる

(これらのメソッドが each を使って実装されている)

Page 28: 怠惰なRubyistへの道 fukuoka rubykaigi01

An Example

class A include Enumerable def each yield 0 yield 1 yield 2 endendA.new.map{¦i¦ i * 2 } # => [ 0, 2, 4 ]

Page 29: 怠惰なRubyistへの道 fukuoka rubykaigi01

ここまでのまとめ

Enumerable は each メソッドを使って多彩なメソッドを追加するなにが「数え上げられる」のかはeach メソッドがどのようにyield を呼び出すかで決まる

Page 30: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerator

Enumerator使ってます?

Page 31: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerator

each に渡すブロックを保留した状態でオブジェクトとして持ち回る

Page 32: 怠惰なRubyistへの道 fukuoka rubykaigi01

to_enum, enum_for

Enumerator の作りかた

• Enumerator.new(obj, method=:each, *args)

• obj.to_enum(method=:each, *args)

• obj.enum_for(method=:each, *args)

• Enumerator.new{¦y¦ ...} ↑これだけちょっと特殊

Page 33: 怠惰なRubyistへの道 fukuoka rubykaigi01

Object#enum_for

each 以外の任意のメソッドの呼び出しをEnumerator にできる(実は Enumerable でなくてもいい)

e_byte = $stdin.enum_for(:each_byte) => バイトごとに yield

e_line = $stdin.enum_for(:each_line) => 行ごとに yield

Page 34: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerator.new{¦y¦}

e = Enumerator.new{¦y¦ y << 0 y << :foo y << 2}

ブロックが each の代わり。ブロックパラメータ y に << すると yield することになる。

Page 35: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerator.new{¦y¦}

class A def each yield 0 yield :bar yield 2 endendA.new.to_enum

と同じ

Page 36: 怠惰なRubyistへの道 fukuoka rubykaigi01

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

Page 37: 怠惰なRubyistへの道 fukuoka rubykaigi01

Method Chain

map, select など一部のメソッドはブロックを省略すると Enumerator を返すので複数のメソッドを組み合わせて使う

ary.map.with_index { ¦v,i¦ ... }

Page 38: 怠惰なRubyistへの道 fukuoka rubykaigi01

Method Chain

[:a, :b, :c, :d].map.with_index do ¦sym, i¦ i.even? ? sym : iend=> [:a, 1, :c, 3] Enumerator

独自のメソッド

Page 39: 怠惰なRubyistへの道 fukuoka rubykaigi01

with_index

[ruby-dev:31953] Matz wrote...

“mapがenumeratorを返すと

obj.map.with_index{¦x,i¦....}

ができるのが嬉しいので導入したのでした。というわけで、

* eachにもmapにもwith_indexが付けられて嬉しい

というのが本当の理由です。”

Page 40: 怠惰なRubyistへの道 fukuoka rubykaigi01

Secret Story of Enumerator.

Enumerator は with_index の為に

生まれた!

Page 41: 怠惰なRubyistへの道 fukuoka rubykaigi01

ここまでのまとめ

• Enumerator•外部イテレータ化

•メソッドチェーン

Page 42: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerator::Lazy

2.0 の新機能

Page 43: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerator::Lazy

Enumeratorのサブクラス

Page 44: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerable#lazy

Enumerator::Lazy の作りかた

Enumerable#lazy を呼ぶ

>> (0..5).lazy

=> #<Enumerator::Lazy: 0..5>

Page 45: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerator::Lazy

Lazyのmap,select,zipなど一部のメソッドがさらにLazyを返す

>> (0..5).lazy.map{¦i¦ i * 2}

=> #<Enumerator::Lazy: #<Enumerator::Lazy: 0..5>:map>

Page 46: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerator::Lazy> Enumerator::Lazy.instance_methods(false)

map collect flat_map

collect_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)]

Page 47: 怠惰なRubyistへの道 fukuoka rubykaigi01

force

Enumrator::Lazy#force

to_a の alias

遅延されているイテレータの評価を実行

Page 48: 怠惰なRubyistへの道 fukuoka rubykaigi01

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 を呼ぶと実行される

Page 49: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumeratorとの違い

Method Chain の使いかたがより強力に

中間データの抑制

Page 50: 怠惰なRubyistへの道 fukuoka rubykaigi01

Pitfall of Enumerator (1)

map, select のようにブロックの戻り値を利用するメソッドを複数繋げることができない(しても意味がなくなる)

(0..5).map.select{¦i¦ i % 2 == 0 }

=> [ 0, 2, 4 ]

↑ map の意味がなくなっている

Page 51: 怠惰なRubyistへの道 fukuoka rubykaigi01

True Method Chain

map や select といったメソッドもいくつでも Method Chain できる。

そう、Lazy ならね。

# 1 から 1000 のうち任意の桁に7を含む数を返す(1..1000).lazy.map(&:to_s).select{¦s¦ /7/ =̃ s}.force

Page 52: 怠惰なRubyistへの道 fukuoka rubykaigi01

Pitfall of Enumerator (2)

map, select のようにブロックの戻り値を利用するメソッドを外部イテレータ化しても意味がない

e = (0..5).mape.next # => 0e.next # => 1e.next # => 2e.next # => 3

↑ map の意味がなくなっている

Page 53: 怠惰なRubyistへの道 fukuoka rubykaigi01

True External Iterator

map などにブロックを渡せるので適用した結果を順に取り出せる

l = (0..5).lazy.select{¦i¦ i.even? }l.next # => 0l.next # => 2l.next # => 4

Page 54: 怠惰なRubyistへの道 fukuoka rubykaigi01

Efficient Memory Usage

Lazy は yield 毎に Method Chain の最後まで処理される

Page 55: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerable version

large_array.map{...}.select{...}

Page 56: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerable version

large_array.map{...}.select{...}

map

Page 57: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerable version

large_array.map{...}.select{...}

map select

Page 58: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerable version

large_array.map{...}.select{...}

map selectGCされる

Page 59: 怠惰なRubyistへの道 fukuoka rubykaigi01

Enumerable version

large_array.map{...}.select{...}

map selectGCされる

map 結果のため

large_array と同じくらいの中間データが必要

Page 60: 怠惰なRubyistへの道 fukuoka rubykaigi01

Lazy version

large_array.lazy.map{...}.select{...}.force

Page 61: 怠惰なRubyistへの道 fukuoka rubykaigi01

Lazy version

large_array.lazy.map{...}.select{...}.force

Page 62: 怠惰なRubyistへの道 fukuoka rubykaigi01

Lazy version

large_array.lazy.map{...}.select{...}.force

GC’ed

Page 63: 怠惰なRubyistへの道 fukuoka rubykaigi01

Lazy version

large_array.lazy.map{...}.select{...}.force

Page 64: 怠惰なRubyistへの道 fukuoka rubykaigi01

Lazy version

large_array.lazy.map{...}.select{...}.force

GC’ed

Page 65: 怠惰なRubyistへの道 fukuoka rubykaigi01

Lazy version

large_array.lazy.map{...}.select{...}.force

Page 66: 怠惰なRubyistへの道 fukuoka rubykaigi01

Lazy version

large_array.lazy.map{...}.select{...}.force

中間データが不要

Page 67: 怠惰なRubyistへの道 fukuoka rubykaigi01

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]

Page 68: 怠惰なRubyistへの道 fukuoka rubykaigi01

Lazy with IO

例) $stdin から空行を読むまでの各行の文字数の配列を返す

$stdin.lazy.take_while{¦l¦ ! l.chomp.empty?}.map {¦l¦ l.chomp.size }

Page 69: 怠惰なRubyistへの道 fukuoka rubykaigi01

Lazy with IO

Enumerable, Enumerator だと空行まで先に読んでしまう。

Lazy なら1行読むたびに take_while/map のブロックが実行される

→ 逐次処理できる

Page 70: 怠惰なRubyistへの道 fukuoka rubykaigi01

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]

Page 71: 怠惰なRubyistへの道 fukuoka rubykaigi01

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

Page 72: 怠惰なRubyistへの道 fukuoka rubykaigi01

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) と書きたい

Page 73: 怠惰なRubyistへの道 fukuoka rubykaigi01

Lazy as a Better Enumerator

Lazy はEnumerator の

正統進化版

Page 74: 怠惰なRubyistへの道 fukuoka rubykaigi01

Happy Lazy Programming!