Scheme ê call/cc ê 用法舉例(2)——coroutine

(接續頂篇,更新日:2019-01-22)

Coroutine

Coroutine(華語:協程[hia̍p-tîng] kap 囝程式 (subroutine) 仝款,lóng 是 kā 程式分節執行 ê 方法。M̄-koh kap 囝程式有呼叫 kap 受呼叫 ê 關係無仝, coroutine 之間是平等 ê ,會當 tī A coroutine 叫 B coroutine,tī B 內底叫 A coroutine。毋過阮這爿為著簡化,干焦展示「主程式 kap coroutine」ê 關係。
Coroutine 跳來跳去運作 ê 展示,作者:Tevfik AKTUĞLU 佇 Wikimedia Commons,授權:[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)([引用源](https://commons.wikimedia.org/wiki/File:Coroutine.png))。

而且,執行 coroutine ê 時,會當 kā 跳 kàu 別个 coroutine,執行伊 ê 時陣 mā 通跳轉來原本 ê coroutine,或者是其他 ê 協程 ê 某 mi̍h 部份。

佇 Python,協程會當用 generator(產生器)kah yield ê 方法來做,舉例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python3
#-*-coding: utf-8-*-

def coroutine():
x = []
while True:
# append the value input to the array x, then return the yield.
#eg. input = a, x = [], then return [a]
input = (yield x)
x = x + [input]

# define coroutine
co = coroutine()
next(co) # to let the coroutine can execute to the yield part to get the value

for i in [0,1,2]:
print("i =", i)
output = co.send(i) # input something to the routine and get the output
print("The output of the coroutine =", output)

執行結果:

i = 0
The output of the coroutine = [0]
i = 1
The output of the coroutine = [0, 1]
i = 2
The output of the coroutine = [0, 1, 2]

有關 tī Scheme 用 call/cc 寫產生器 ê 舉例,通參考這篇佇 IThome ê 教學,以下 ê 實例是展講 call/cc 通製造通相轉 ê code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#lang racket
(define (coroutine1)
(display "Coroutine 1 part 1\n")

; Coroutine 1 第 1 ê 切換點
(call/cc
(lambda (cc-1-1)
(set! coroutine1 cc-1-1) ; 長 (tióng) 賰 ê beh 計算 ê 部份 (cc-1-1 以後ê部份) kàu coroutine1
(coroutine2) ;跳 kah coroutine2 ê 起頭
))
(display "Coroutine 1 part 2\n")

; Coroutine 1 第 2 ê 切換點
(call/cc
(lambda (cc-1-2)
(set! coroutine1 cc-1-2)
(coroutine2) ;跳 kàu cc-2-1
))
; Coroutine 1 第 3 ê 切換點
(display "Coroutine 1 part 3\n")
(call/cc
(lambda (cc-1-3)
; 長賰 ê 部份 (display "End of coroutine.") kàu coroutine1
(set! coroutine1 cc-1-3)
(coroutine2)))

(display "End of coroutine.") ; 結束 coroutine1
)

(define (coroutine2)
(display "Coroutine 2 part 1\n")
(call/cc
(lambda (cc-2-1)
(set! coroutine2 cc-2-1) ;長賰 ê beh 計算ê部份 (cc-2-1 以後) kàu coroutine2
(coroutine1) ; 跳 kàu coroutine1 (cc-1-1)
))

(display "Coroutine 2 part 2\n")
(call/cc
(lambda (cc-2-2)
(set! coroutine2 cc-2-2)
(coroutine1)
))

(display "Coroutine 2 part 3\n")
(call/cc
(lambda (cc-2-3)
(set! coroutine2 cc-2-3)
(coroutine1))) ; 跳 kàu coroutine1 上尾,結束
)

(coroutine1) ; 對 coroutine1 ê 起頭開始執行

執行結果:

Coroutine 1  part 1
Coroutine 2 part 1
Coroutine 1 part 2
Coroutine 2 part 2
Coroutine 1 part 3
Coroutine 2 part 3
End of coroutine.

咱人會當用 call/cc 記錄 coroutine 執行 kàu toh,就 kā 伊 ê 繼續寫 kàu coroutine,閣跳到別个 coroutine (beh 恢復執行 ê 點),來一直切換 coroutine ê 執行。

結論

call/cc 會當 the̍h 來合成真濟控制方法,毋過因為愛 kā 某一个時間 ê 繼續更新 kàu 狀態變數,對追求袂變得 ê 物件 (immutable object) ê 正港 ê 函數式程式設計語言,是有相踏脫[sio-ta̍h-thut] ê 所在,所以準若 Haskell 等「純函數式語言」有這號函數,凡勢功能無遐爾大,我咧[ioh]

用 call/cc 可能出現無欲愛 ê 運作,甚至是死循環,tse 愛注意。