Ruby2.0の遅延リスト(Enumerator::Lazy) #tsukubarb

17
Ruby2.0 の遅延リスト 2014/05/08 @gam0022

description

Ruby2.0の遅延リスト(Enumerator::Lazy) を紹介します。 つくば.rb で発表しました。http://www.zusaar.com/event/4597003

Transcript of Ruby2.0の遅延リスト(Enumerator::Lazy) #tsukubarb

Page 1: Ruby2.0の遅延リスト(Enumerator::Lazy) #tsukubarb

Ruby2.0の遅延リスト2014/05/08 @gam0022

Page 2: Ruby2.0の遅延リスト(Enumerator::Lazy) #tsukubarb

自己紹介

@gam0022

coins11(情報科学類4年)

NPAL(非数値処理アルゴリズム研究室)

COJT SW 5th(去年)

Ruby と C# が好き

Page 3: Ruby2.0の遅延リスト(Enumerator::Lazy) #tsukubarb

自己紹介(TwinCal)

TwinCal

“TwinCal"ググれば出てくる

Twinsの履修情報から、Googleカレンダー・iCalの時間割を作成するWebサービス

Page 4: Ruby2.0の遅延リスト(Enumerator::Lazy) #tsukubarb

自己紹介(大五郎Bot)

大五郎Bot

マルコフ連鎖でフォローユーザのツイートを学習して喋るBot

Rubyで実装

Favstar から BAN ←去年5月

Page 5: Ruby2.0の遅延リスト(Enumerator::Lazy) #tsukubarb

自己紹介(大五郎Bot)3回目の凍結中 ←New(今ここ)

永久凍結の危機(フォローワーが5000くらいなので惜しい)

Page 6: Ruby2.0の遅延リスト(Enumerator::Lazy) #tsukubarb

Rubyの遅延リスト

Ruby 2.0 から導入された遅延リスト

Enumerable#lazy というメソッド

その返り値の Enumerator::Lazy

について紹介

Page 7: Ruby2.0の遅延リスト(Enumerator::Lazy) #tsukubarb

遅延リスト

遅延リストとは

Haskell などの一部の関数型言語では一般的に使われている

遅延評価といって、値が必要になるまで計算しないことによって、無限に続くリストも扱うことができる

Page 8: Ruby2.0の遅延リスト(Enumerator::Lazy) #tsukubarb

使用例

lazy を付けると無限リストが扱える!

[1]

[2]

Page 9: Ruby2.0の遅延リスト(Enumerator::Lazy) #tsukubarb

[1]はなぜダメか

(1..Float::INFINITY).map{|n| n*2}.first(5)

Enumerator#map -> Array

Enumerator#map は配列を返すメソッド

➡無限の長さの配列を生成しようとして死ぬ

first(5)は実行されない (map待ち)

Page 10: Ruby2.0の遅延リスト(Enumerator::Lazy) #tsukubarb

[2]の解説

(1..Float::INFINITY).lazy.map{|n| n*2}.first(5)

Enumerator::Lazy#map -> Enumerator::Lazy

Enumerable#lazy はリスト(ArrayやRangeなど)をEnumerator::Lazy に変換するメソッド

Enumerator::Lazy#map も Enumerator::Lazy を返す

Enumerable#lazy -> Enumerator::Lazy

Page 11: Ruby2.0の遅延リスト(Enumerator::Lazy) #tsukubarb

[2]の解説

(1..Float::INFINITY).lazy.map{|n| n*2}.first(5)

Enumerator::Lazy#first -> Array

Enumerator::Lazy#first でようやく値が評価される(Arrayになる)

Enumerator::Lazy の中身は force(to_a), first などが呼ばれるまで、値が評価されない

Page 12: Ruby2.0の遅延リスト(Enumerator::Lazy) #tsukubarb

Enumerator#mapとEnumerator ::Lazy#map

[1]と[2]の map メソッドはそれぞれ全く別物

[1] Enumerator#map -> Array

[2] Enumerator::Lazy#map -> Enumerator::Lazy

定義されている場所も返り値の型も違う

lazy を付けると無限リストが扱えるという理解でも良いが、正しくは、Enumerator::Lazy に定義された lazy 版の map を呼び出している。

Page 13: Ruby2.0の遅延リスト(Enumerator::Lazy) #tsukubarb

lazyを付ける不思議な仕様

なぜ、lazy を付けると map の動作が変わる不思議な動作にしたのか?

Enumerable#lazy を作った 原 悠 さんによると、

Enumerable モジュールには、lazy 版がほしくなるようなメソッドが map、select、reject、drop、... とたくさんある。

全部追加すると、Enumerable モジュールのメソッドが増えすぎる。

Enumerator::Lazy 名前空間を提供することによって、「lazy」というメソッド名をひとつ追加するだけで lazy 版の map や select などが使えるようにした。

http://magazine.rubyist.net/?0041-200Special-lazy

Page 14: Ruby2.0の遅延リスト(Enumerator::Lazy) #tsukubarb

遅延リストが使える場面

遅延リストが有効に使える場面

数学的に無限長の数列を扱いたい

TwitterのTimeLine

巨大なファイル(の一部だけ処理したい)

Page 15: Ruby2.0の遅延リスト(Enumerator::Lazy) #tsukubarb

遅延リストで綺麗なコード

従来の Range#each とかでも最初の例と同じことができるが、map が使えると綺麗・簡潔にコードが書ける

map, first というように各処理が抽象化されている方が読みやすい

Page 16: Ruby2.0の遅延リスト(Enumerator::Lazy) #tsukubarb

まとめ

Enumerable#lazy はリスト(ArrayやRangeなど)を遅延リストEnumerator::Lazy に変換するメソッド

遅延リストを使うと、無限リストもシンプルに扱える

Enumerator::Lazy#map は Enumerator#map のlazy 版の全く別のメソッド

Enumerable#lazy と Enumerator::Lazy#map を積極的に使おう!

Page 17: Ruby2.0の遅延リスト(Enumerator::Lazy) #tsukubarb

補足

継承関係が複雑なのでまとめました(細かい部分は省略)

BasicObjectKernelObject

Enumerable

Enumerator Enumerator::Lazy

{- lazy - map - select - first,…⋯

{- map - select - first,…⋯

Range

Array

lazy版に再定義