More Related Content
Similar to Ruby1.9のfiberのかっこよくつかおう
Similar to Ruby1.9のfiberのかっこよくつかおう (16)
More from Kindai University
More from Kindai University (20)
Ruby1.9のfiberのかっこよくつかおう
- 1. e-Zuka Teck Night Vol.1
Ruby の
クロージャと
ファイバーを
かっこよく使ってみよう
e-Zuka Teck Night, Rubyist九州 近畿大学
山崎重一郎
- 2. e-Zuka Teck Night Vol.1
Rubyには
関数型言語の
顔があるんです
1.9になってますます関数型言語的な感じ
closureやFiberはそんな感じが強い
今日はRubyをオブジェクト指向言語じゃなくて関
数型言語みたいに扱います!
- 5. e-Zuka Teck Night Vol.1
Rubyの文法をちょっと
繰り返し構造を持つオブジェクトの例
イテレータ
(1..5) #範囲ブジェクト
オブジェクト自体の繰り返し構造にそって繰り返しを制御
[1,2,3,4,5] #配列
> (1..5).map{|x| x*100} #イテレータmap
[100, 200, 300, 400, 500]
> (1..5).reduce{|s,x|s+=x} #イテレータreduce
15
- 6. e-Zuka Teck Night Vol.1
関数に名前をつける
Procを代入する
階乗関数の例
fact =->n{n==0?1:(1..n).reduce(:*)}
- 7. e-Zuka Teck Night Vol.1
関数とクロージャ
関数の独立変数と係数は何が違うのか?
f(x) = a*x
アルファベットの前の方が係数で終わりの方が変数?
- 8. e-Zuka Teck Night Vol.1
λとレキシカル変数
ラムダは「これが変数だ」という束縛を表示する
f = ->x{a*x}
束縛されていない変数 aは、「レキシカル変数」
- 10. e-Zuka Teck Night Vol.1
レキシコン:辞書の語彙
レキシカル変数とは、辞書の項目の意味
文字列 → 束縛されている値
eval するとその環境の辞書におけるレキシカル変数の
値がわかる
> age = 54
> eval('age')
54
- 12. e-Zuka Teck Night Vol.1
レキシカル変数と
クロージャ
係数 a を関数の関数の束縛変数として定義する場合
f= ->a{->x{a*x}}
- 13. e-Zuka Teck Night Vol.1
関数の関数でクロージャを
つくる
組合せ関数 2引数(nとr)
combi=->n{->r{fact[n]/(fact[r]*fact[n-r])}}
> combi[8][4]
70
> c8=combi[8] # nが8のクロージャをつくる
> (0..8).map{|x| c8[x]} #いろんなrでの結果
[1, 8, 28, 56, 70, 56, 28, 8, 1]
- 14. e-Zuka Teck Night Vol.1
関数に辞書(環境)が含ま
れている(クロージャ)
> eval('n',c8.binding)
>8
> n=1000
> eval('n',c8.binding)
>8
- 15. e-Zuka Teck Night Vol.1
レキシカルスコープ
変数の辞書に一旦定義されると
その後は、ずっと内側の世界でも同じ辞書を使う
内側の世界の辞書は外からは隠蔽されている
a=1
a=10 代入 a=10
a→10 a→未定義
a→10 a→未定義
- 16. e-Zuka Teck Night Vol.1
関数のメモ化は、xが未定義の時だけobj
x||=obj
クロージャでキャッシュが作れる
を代入するという意味
組み合わせ関数のメモ化
c=->m{->n{m[n]||={};->r{m[n][r]||=combi[n][r]}}}
> cm=c[{}] #空のハッシュオブジェクトで初期化
> cm[80000][79990]
2957280616639006757538254376469462127992000
> eval('m',cm.binding)
{80000=>{79990=>2957280616639006757538254376
469462127992000}}
- 17. e-Zuka Teck Night Vol.1
Fiber #子スレッド(ファイバー)
f=Fiber.new{|x|
puts '最初'
Fiber.yield
軽量スレッドと継続 puts x
• Fiber.new{|x|...} y=Fiber.yield
ファイバーの生成 puts y
=子スレッド(軽量) }
#親スレッド
• Fiver.yield(obj)
> f.resume 3
親のコンテクストに行く
最初
> f.resume
• resume(obj) メソッド 3
子供のコンテクストに行く > f.resume 7
(停止中の処理を継続) 7
> f.resume
FiberError: dead fiber called
- 18. e-Zuka Teck Night Vol.1
Fiberによる
親子コミュニケーション
ファイバー(子スレッド)の生成
f=Fiber.new{|x| loop{x=Fiber.yield(x+1)}}
親スレッドからファイバー(子スレッド)にメッセージを出すと
ファイバーがそれに応える
> f.resume 100
101
> f.resume 2
3
- 19. e-Zuka Teck Night Vol.1
Fiberによるジェネレータ
無限集合などをあつかうプログラム
自然数ジェネレータ
num=->a{loop{a+=1}}
> num[0] #... 無限ループ〜
無限集合を素直に生成しているんだけどね
ファイバーにした自然数ジェネレータ
num=Fiber.new{|a| loop{Fiber.yield a+=1}}
> num.resume 0
1
> num.resume
2
- 20. e-Zuka Teck Night Vol.1
Fiberによるジェネレータ
フィボナッチ数列バージョン!
fib=->{a,b=0,1;loop{a,b=b,a+b}}
>fib[]
... 無限ループ〜
ファイバーにする
f=Fiber.new{a,b=0,1;loop{Fiber.yield a;a,b=b,a+b}}
> 5.times {puts f.resume}
0
1
1
2
3
- 21. e-Zuka Teck Night Vol.1
Generate and Test
素数生成の例
> num=->a{Fiber.new{loop{Fiber.yield a+=1}}}
> numg=num[1] #2以上の自然数のジェネレータ
prime=->pl{->{
begin
n=numg.resume #自然数ジェネレータ
end until pl.all?{|x|n%x!=0} #テスト
pl.push n}}
> pr=prime[[]]
> pr[]
[2]
> pr[]
- 22. e-Zuka Teck Night Vol.1
Fiber による
ジェネレータ
素数バージョン(しつこい!)
prime =->n{p=[];loop{p.push n if p.all?{|x|n%x!=0};n+=1}}
>prime[2]
ファイバー版
fprime=Fiber.new{|n|p=[];
loop{Fiber.yield p.push(n).last if p.all?{|x|n%x!=0};n+=1}}
> fprime.resume 2
2
> fprime.resume
3
- 23. e-Zuka Teck Night Vol.1
ノンプリエンプティブな
マルチタスク
OSに頼らない(負荷が軽い)
ファイバーにした自然数ジェネレータ
num1=Fiber.new{|a| loop{Fiber.yield a+=1}}
num2=Fiber.new{|a| loop{Fiber.yield a+=1}}
> num1.resume 0
1
> num2.resume 0
1
> num1.resume
2
> num2.resume
2
- 24. e-Zuka Teck Night Vol.1
コルーチンとしての
Fiber
初期のMacintosh OS
多数のコルーチンの集合体でできていた
Macintosh 128K
(超軽量スレッド)
8MHz
128Kb
操作による
イベント
実際のメモリ
ビットマップ
への表示など
- 25. e-Zuka Teck Night Vol.1
Fiberでコルーチン
Webサーバの超軽量化
イベント処理をコルーチンPoolに事前に生成しておく
メモリ節約、レスポンスの高速化
Pjaxなどの
細粒度イベント
ブラウザ
操作による
イベント
POSTとか
Fiber対応Webサーバ
ブラウザへの
Goliathとか 表示変更
- 26. e-Zuka Teck Night Vol.1
FiberとThreadの比較
出典:Ruby Fibers Vs Ruby Threads
http://oldmoe.blogspot.com/2008/08/ruby-fibers-vs-ruby-threads.html
- 27. e-Zuka Teck Night Vol.1
Fiberでリアルタイム処理
3軸加速度センサーのバッファ入力の例
#バッファ管理クロージャ
bf=->a{->d{a.push(d).shift; a}}
#ファイバー生成関数
bff=->a{b=bf[a]; Fiber.new{|d|loop{d=Fiber.yield b[d]}}}
fx=bff[Array.new(10,0.0)] #x軸方向
fy=bff[Array.new(10,0.0)] #y軸方向
fz=bff[Array.new(10,0.0)] #z軸方向
loop do
data=sp.readline.split(',').map {|x| x.to_f}
fx.resume(data[0])
fy.resume(data[1])
fz.resume(data[2])
end
- 28. e-Zuka Teck Night Vol.1
Fiberによる
リアルタイム処理
3つじゃあまりありがたくない
1000個のセンサーの入力をサーバでリアルタイム処理
群衆や流体のシミュレーションにも
JavaScritptやprocessingの方がいいかもしれないけど
- 29. e-Zuka Teck Night Vol.1
コールバックスパゲティ
を改善する
JavaScriptでブラウザの制御を書くと
コルーチン登録タイミングの空振り
コールバックルーチンがコールバックルーチンを呼ぶ
javaScript 1.7で Fiber (yield, next) が仕様に追加され
た(いまのところ実装されているのはfirefoxだけ)
将来的には、マウスでぐりぐりやるようなプログラムはFiber
で書くようになるでしょう。
- 30. e-Zuka Teck Night Vol.1
まとめ
細粒度イベント駆動型Web (Pjaxなど)
センサー/機械制御系/ロボット
シミュレーション
ジェネレータを使う知的システム
マウスでごりごり型
ファイバーをうまく使うとそういうタイプのプログラ
ムをすっきり書ける!