Ruby 的 " 延續 " 觀念 : Continuation
試著想想這個程式(出自 Beyound JAVA 範例)
這是這個程式的結果,很出人意料之外吧
現在可以討論這個程式了。
這個機制怎麼玩呢?
我們可以想到,程式執行到一半,有個 interrupt,他出去執行一下 error handler ,然後回來繼續處理剛剛的事情,當然我們當然可以用其他方式做到類似的事情,不過這個作法的確提供很方便的方式。
根據 Wikipedia 的講法,Lisp ,Ruby ,Smalltalk 都有提供類似的作法(果然 Ruby 是 Perl + SmallTalk + Lisp ),但是我比較好奇 C 也有這種作法?
改天我會提到利用這個觀念,產生的延續性伺服器的想法。
想想這個程式會怎麼做?
1 def con_loop
2 for i in 1..5 do
3 puts i
4 callcc { |a| return a } if i == 2
5 puts '#'
6 end
7 return nil
8 end
9 puts 'Before loop call'
10 cont = con_loop()
11 puts 'After loop call'
12 cont.call if cont
13 puts 'After continuation call'
這是這個程式的結果,很出人意料之外吧
Before loop call為什麼 After loop call 執行了兩次?為什麼 1 2 跟 3 4 5 中間夾了一個 After Call。這就講到一個有趣的物件 Continuation,他是一個物件,裡面記載著程式運作的狀態
1
#
2
After loop call
#
3
#
4
#
5
#
After loop call
After continuation call
沒錯,他記載了程式運行的狀態!!!好像遊戲打到一半,媽媽叫你去吃飯,你只好心不甘情不願的去存檔。可是吃完飯,load 剛剛存的檔,你的角色的經驗值都還在,寶物都不會不見一樣。Continuation 就是那個遊戲的紀錄檔
現在可以討論這個程式了。
- i 進入loop裡面 ,puts 1 ,puts 2。
- 當 i = 2 時,下一行也就是第四行,因為 if i == 2 這個敘述的緣故( 媽媽叫你吃飯了)
- 會被強迫跳出這個 loop ( return a ) (只好關機)
- callcc { |a| return a } 他會 return 一個 continuation 物件,continuation 裡面的內容就是你遊戲的紀錄檔,他會在 10行裡面,存到 cont 這個 Object 裡面 (存檔等下再來玩),所以程式會 puts 1 , 2 之後,就進入第11行的 puts 'After loop call',這也是第一次的 After loop call。
- 第12行,他會檢查 cont 這個物件有沒有值 (檢查存檔在不在)
- 發現到有值,就執行 Continuation 物件的 call method (呼叫存檔)
- 他會到 loop function 裡面 ,剛剛 return continuation 物件的地方。最重要的是他會紀錄 i 這個 tmp variable 是 2,因為他本來就會紀錄程式狀態。
- 執行第五行的 puts '#',這也是為啥,結果裡面第一個 After loop call 後面是 #。
- 他會繼續的進行 loop ,從 i =3 ~ 5
- 最後執行結束,他會在第7行 return null 值,交給 cont 物件。
- 之後他就會執行第二次的 After loop call。
這個機制怎麼玩呢?
我們可以想到,程式執行到一半,有個 interrupt,他出去執行一下 error handler ,然後回來繼續處理剛剛的事情,當然我們當然可以用其他方式做到類似的事情,不過這個作法的確提供很方便的方式。
根據 Wikipedia 的講法,Lisp ,Ruby ,Smalltalk 都有提供類似的作法(果然 Ruby 是 Perl + SmallTalk + Lisp ),但是我比較好奇 C 也有這種作法?
- C:
setcontext
et al. (UNIX System V and GNU libc)
改天我會提到利用這個觀念,產生的延續性伺服器的想法。
1 則留言:
真的蠻………難消化的…是說callcc回傳的物件被call的那一瞬間,程式會回到當初callcc被呼叫的那一點嗎?
張貼留言