30分でわかるcallccの使い方 yhara (原 悠) 京大マイコンクラブ.

Slides:



Advertisements
Similar presentations
山元進.  for 文  while 文  do ~ while 文  文のネスト  break 文  continue 文.
Advertisements

わんくま同盟 福岡勉強会 #4 yield について るーごん. わんくま同盟 福岡勉強会 #4 自己紹介 日本一人口が少ない県に住んでいます。 一昨年まで、ちょっと福岡に住みました。 仕事は主に dbMAGIC 。 プログラミング言語はよく分かりません。 好きなもの ポケモン、ファイブマン、ジェットマン、
2007/12/12 ema.
4章 制御の流れ-3.
プログラミング基礎I(再) 山元進.
オブジェクト指向プログラミング(4) 静的分析(2)
Ex7. Search for Vacuum Problem
スレッドの同期と、スレッドの使用例 スレッドの同期 Lockオブジェクト: lockオブジェクトの生成
2008/03/01 D-BOF k.inaba はじめての initial D 2008/03/01 D-BOF k.inaba
Ex8. Search for Vacuum Problem(2)
実行時のメモリ構造(1) Jasminの基礎とフレーム内動作
Lightweight Language Weekend ls-lRシェル
吉田和弘 株式会社ミッタシステム Rubyのすすめ 吉田和弘 株式会社ミッタシステム
多重ループ 繰り返し構造:補足事項 第8回目 [6月8日、H.16(‘04)] 本日のメニュー 1)前回の課題について
多重ループ 繰り返し構造:補足事項 第8回目 [6月12日、H.15(‘03)] 本日のメニュー 1)前回の課題について
最適化ソルバーのための Python言語入門
第2回:Javaの変数と型の宣言 プログラミングII 2007年10月2日.
Ruby勉強会(第1回) 2006/06/29 竹内豪.
Boost.勉強会 #8 大阪 ( ) C++ Tips 3 カンマ演算子編.
ピカチュウによる オブジェクト指向入門 (新版)
細かい粒度でコードの再利用を可能とするメソッド内メソッドのJava言語への導入
0からわかるF# Part1 中 博俊 F# September 2008 CTP Base.
0からわかるF# Part1 中 博俊 F# September 2008 CTP Base.
動的スライスを用いた バグ修正前後の実行系列の比較
第3回 2007年4月27日 応用Java (Java/XML).
関数の定義.
電気・機械・情報概論 VBAプログラミング 第2回 2018年7月2日
0からわかるF# Part1 中 博俊 F# September 2008 CTP Base.
地域情報学演習 VBAプログラミング 第3回 2017年10月24日
プログラミング 4 記憶の割り付け.
二分木のメソッド(続き).
アルゴリズムとプログラミング (Algorithms and Programming)
0からわかるF# Part1 中 博俊 F# September 2008 CTP Base.
Structured programming
第2回 2007年4月20日 応用Java (Java/XML).
データ構造と アルゴリズム 第五回 知能情報学部 新田直也.
プログラミング 4 整列アルゴリズム.
0からわかるF# Part1 中 博俊 F# September 2008 CTP Base.
プログラミング言語論 第四回 理工学部 情報システム工学科 新田直也.
Ex7. Search for Vacuum Problem
0からわかるF# Part1 中 博俊 F# September 2008 CTP Base.
再帰的手続き.
プログラムの基本構造と 構造化チャート(PAD)
東京工科大学 コンピュータサイエンス学部 担当 亀田弘之
東京工科大学 コンピュータサイエンス学部 担当 亀田弘之
IF文 START もしも宝くじが当たったら 就職活動する 就職活動しない YES END NO.
プログラミングⅡ 第2回.
プログラムが実行されるまで 2002年4月14日 海谷 治彦.
C#プログラミング実習 第3回.
オープンソースソフトウェアに対する コーディングパターン分析の適用
計算機プログラミングI 第4回 2002年10月31日(木) 問題解決とアルゴリズム クラスメソッドと手続きの抽象化 最大公約数
高度プログラミング演習 (11).
復習 if ~ 選択制御文(条件分岐) カッコが必要 true 条件 false 真(true)なら この中が aを2倍する 実行される
第5章 まだまだ続く反復処理!! ~繰り返しその2 for~
東京工科大学 コンピュータサイエンス学部 担当 亀田弘之
コンパイラ 2012年10月11日
プログラミング入門2 第6回 関数 情報工学科 篠埜 功.
オブジェクト指向言語論 第七回 知能情報学部 新田直也.
岩村雅一 知能情報工学演習I 第10回(後半第4回) 岩村雅一
Javaとは Javaとはオブジェクト指向言語でJava VM(Java仮想マシン)と呼ばれるプログラム上で動作します。
プログラミング演習I 2003年6月11日(第9回) 木村巌.
プログラミング序論演習.
君ならどうする – ls-lRシェル Python編
プログラミング入門2 第3回 条件分岐(2) 繰り返し文 篠埜 功.
オブジェクト指向言語論 第十回 知能情報学部 新田直也.
情報処理Ⅱ 第3回 2004年10月19日(火).
情報処理Ⅱ 2006年10月20日(金).
プログラミング演習I 補講用課題
プログラミング 2 静的変数.
情報処理技法(Javaプログラミング)1 第8回 同じ処理を何回も繰り返すには?
Presentation transcript:

30分でわかるcallccの使い方 yhara (原 悠) 京大マイコンクラブ

今日のまとめ (1) callccって何? (2) callccは凄い! (3) callccは危ない!

キーワード:継続、Continuation、callcc

プログラムの基本要素 (1) 順次実行 (2) 分岐 (3) 繰り返し

プログラムの基本要素 (1) 順次実行 (2) 分岐 (3) 繰り返し (4) ワープ

ワープ?

class Foo def f p 1 callcc{|cc| return cc} p 2 end class Bar def initialize @cc = Foo.new.f def g p 3 @cc.call p 4 Bar.new.g

Matz…?

class Matumoto 王様と話すとセーブできる ドラゴンのこうげき! 89のダメージ まつもとはしんでしまった 王様「まつもとよ、しんでしまうとは情けない」 →セーブしたところからやりなおし

callccはセーブポイントに似ている callccでセーブ、cc.callでロード $cc = nil def hoge print 1 callcc{|cc| $cc = cc} print 3 end hoge $cc.call callcc=セーブ (ccがセーブポイント) ccはContinuationクラスの インスタンス cc.call=ロード

callccはセーブポイントに似ている callccでセーブ、cc.callでロード $cc = nil def hoge print 1 callcc{|cc| $cc = cc} print 2 end hoge $cc.call callcc=セーブ (ccがセーブポイント) ccはContinuationクラスの インスタンス cc.call=ロード

callccはセーブポイントに似ている callcc{}の返り値: cc.call(arg)で飛んできたときはarg そうでない時(最初の一回)はブロックの返り値 $cc = nil def hoge print 1 print callcc{|cc| $cc = cc; 2} print 3 end hoge $cc.call(4) # 1 2 3 4 3 4 3 4 … と出力される

まとめ セーブ callcc{|cc| … } # ccがセーブポイント ロード cc.call callccの「次の処理」から再開される

callccは凄い!

(1) 3重ループを一発で脱出 for i in (0..10) for j in (0..10) for k in (0..10) if i==j && j==k #ループを抜け出したい! end

(1) 3重ループを一発で脱出 callcc{|cc| for i in (0..10) for j in (0..10) for k in (0..10) if i==j && j==k cc.call end } ここ(callccの次の処理) に飛ぶ

それcatchでできるよ catch/throw catch(:escape){ for i in (0..10) for j in (0..10) for k in (0..10) if i==j && j==k throw :escape end }

(2) メソッドを「少しずつ」実行する ゲームのイベント処理 wait_okをどのように実装すればいい? def event until input[”ok”] #これは他の処理が sleep 1 #止まってしまうのでダメ end def event king.say(“おお#{@name}よ、しんでしまうとはふがいない”) wait_ok king.say(“そなたにもういちどきかいをあたえよう”) king.say(“では ゆけ #{@name}よ!”) end

(´・ω・`) def event(input) case @step when 0 king.say “おお#{@name}! しんでしまうとはふがいない” @step += 1 when 1 @step += 1 if input[“ok”] when 2 king.say “そなたに もういちど きかいを あたえよう” when 3 when 4 king.say “では ゆけ #{@name}よ!” end

callccを使うと… メソッドを「少しずつ」実行できる wait_okが呼ばれたたら、セーブしてreturn 次回の呼び出しはセーブしたところから再開 def event king.say(“おお#{@name}よ、しんでしまうとはふがいない”) wait_ok king.say(“そなたにもういちどきかいをあたえよう”) king.say(“では ゆけ #{@name}よ!”) end

(3) 全探索を簡単に あるマンションに5人の男が住んでいる bakerは5Fではない cooperは1Fではない millerはcooperより上の階にいる smithとfletcherは1つ隣の階にいる…

ありがちな方法 for baker in 1, 2, 3, 4, 5 for cooper in 1, 2, 3, 4, 5 for fletcher in 1, 2, 3, 4, 5 for miller in 1, 2, 3, 4, 5 for smith in 1, 2, 3, 4, 5 if baker != 5 && cooper != 1 && fletcher != 1 && fletcher != 5 && miller > cooper return [baker,cooper,fletcher,miller,smith] end

callccを使うと… require "amb“ A = Amb.new baker = A.choose(1, 2, 3, 4, 5) #⇒ 1~5の数字を順に選んでくれる cooper = A.choose(1, 2, 3, 4, 5) fletcher= A.choose(1, 2, 3, 4, 5) miller = A.choose(1, 2, 3, 4, 5) smith = A.choose(1, 2, 3, 4, 5) A.assert([baker, cooper, fletcher, miller, smith].uniq.length == 5) A.assert(baker != 5) A.assert(cooper != 1) A.assert(fletcher != 1 && fletcher != 5) A.assert(miller > cooper) A.assert((smith - fletcher).abs != 1) A.assert((fletcher - cooper).abs != 1) p [baker, cooper, fletcher, miller, smith]

(4) eachを「少しずつ」回す C++風のイテレータ g = Generator.new([1,2,3]) while g.next? requrie ‘generator’ (標準添付) g = Generator.new([1,2,3]) while g.next? p g.next end

(5) ppp irb> p x pデバッグ 3 変数名を書くとわかりやすい でも面倒 irb> p ”x=#{x}” irb> ppp :x pデバッグ 変数名を書くとわかりやすい でも面倒 自動でできないか? 変数の値を調べるには bindingというメソッドを使う

呼び出し元のbindingが欲しい def ppp(symbol) set_trace_func{|*args| .. cc.call(eval(“binding”)} b = callcc{|cc| ..} if state == :prepare return else puts ”#{symbol} = #{eval(symbol, b)}” end a = 1 ppp :a Ruby界の3大黒魔術が夢の競演! ・eval系 (eval, *_eval) ・フック系 (*_missing, set_trace_func) ・callcc ※このコードは抜粋です

呼び出し元のbindingが欲しい def ppp(symbol) set_trace_func{|*args| .. cc.call(eval(“binding”)} b = callcc{|cc| ..} if state == :prepare return else puts ”#{symbol} = #{eval(symbol, b)}” end a = 1 ppp :a

こんなもん誰が考えたんだ Binding.of_caller として Rails (ActiveSupport)で導入されたのが初出? 1.8.5以降だと動かない…。 pppの配布元はこちら http://www.rubyist.net/~rubikitch/computer/ppp/

callccは危ない!

なぜ危ない? (Rubyの) バグの原因になりやすい 油断するとRubyが落ちる 「次のようにすると core を吐きます」祭り Rubyist Magazine – Rubyの落とし方 (akr) http://jp.rubyist.net/magazine/?0002-RubyCore [BUG] Segmentation fault:

例 foo do puts ”hello” puts ”world” end

例 static VALUE rb_foo(VALUE ary1) { int *p = malloc(...); … rb_yield(); free(*p); } foo do puts ”hello” puts ”world” end

例 static VALUE rb_foo(VALUE ary1) { int *p = malloc(...); … rb_yield(); free(*p); } foo do callcc{|$cc| …} puts ”hello” puts ”world” end $cc.call

例 static VALUE rb_foo(VALUE ary1) { int *p = malloc(...); … rb_yield(); free(*p); } foo do callcc{|$cc| …} puts ”hello” puts ”world” end $cc.call 2回freeされてしまう

まとめ

callccを使うと スパゲッティコードが簡単に書ける!

callccを使うと スパゲッティコードが簡単に書ける!

まとめ callccはとても強力 でも他人に読めないコードになりやすい callccは楽しい でもRubyインタプリタのバグ要因になり易い 処理の流れを自在に操れる でも他人に読めないコードになりやすい 見えないところに隠そう callccは楽しい 思いもよらないことができる でもRubyインタプリタのバグ要因になり易い 将来無くなるかも

おまけ Fiberについて

callccとFiber callccの代表的な使い方: callccが危険なのは(B)が可能だから (A) 処理の中断/再開 (generator, wait_ok) (B) 処理のやり直し (amb, ppp) callccが危険なのは(B)が可能だから じゃあ(A)の機能だけなら残してもいいかも? というわけで、1.9にFiberが実装された

Fiber foo = Fiber.new{ p 2 Fiber(繊維)Thread(糸) Fiber.prev.yield } bar = Fiber.new{ p 1 foo.yield p 3 bar.yield #1 2 3の順に表示される Fiber(繊維)Thread(糸) Threadとの違い newしただけでは実行されない 勝手にスイッチされない Fiber#yield で移動 Fiberの特徴 処理を途中で止める ことができる 直前のFiberを取れる (Fiber.prev)

1.9はどうなる? Ruby1.9は継続と“Fiber”をサポート - @IT あり得る選択肢: 今後に注目 …というのはガセ(サポートすると決まったわけではない) あり得る選択肢: callccもFiberも残る Fiberだけが残る callccだけが残る callccもFiberも残らない 今後に注目 1.9.xは今年のクリスマスにリリース予定です

ご清聴ありがとうございました ご質問は?