如何實現 Call / CC 或者陰陽謎題(Yin Yang Puzzle)?

源代碼好像是來自王垠的博客

(let* ((yin
((lambda (cc) (display #@) cc) (call-with-current-continuation (lambda (c) c))))
(yang
((lambda (cc) (display #*) cc) (call-with-current-continuation (lambda (c) c)))))
(yin yang))

這當然是一個死循環啦,不斷地輸出@*@**@***@****...

想請教的是,如何使用指令式的語言實現呢? 最好選擇靜態語言啦。。。例如C++, C#, Java, JS 等等。其實用F#,Ruby也都可以啦,看大家方便,謝謝大家 (抱拳

這裡有一些鏈接可能會有些幫助

Wikepedia of Call / CC

Cont signature document of SML of NJ

A discussion/explanation of Yin Yang Puzzle in StackOverflow

A detailed blog about Yin Yang Puzzle

Await vs Continuation in C#, a discussion in StackOverflow

我很努力地嘗試用C#去實現,但是實在愚鈍,做不到啊...還請大家幫幫忙。 還有順便一問,有什麼別的辦法能訪問王垠的博客嗎?還有很多很精彩的技術博文沒看呢。。。

delegate void YinYang(YinYang y = null);
static YinYang Yin = (f) =&> { Console.Write("@"); Yang(f); };
static YinYang Yang = (f) =&> { Console.Write("*"); Yin(f); }; //Try IEnumerable&, or IEnumerable& to simulate the environment?!
Yin(Yang);

==========================

請允許感謝一下,並小結一下。我自己平時是寫C#的,所以特別感謝老趙 @趙劼, 還有@bhuztez 很精彩的圖表分析外加Erlang 版,@Zete 的Ruby 版。向各位大神學習,編程我還要多加學習。

就輸出的內容本身是沒有什麼意義的,樓下也有人使用了循環嵌套來實現。我覺得從靜態語言,如C# 的角度上看,這個問題我看有兩個難點: 第一, delegate 可以 用同類的delegate 來做 輸入類型,並且輸出同樣的delegate,我最早一直用Func&, Func&&>,始終無法實現。第二,是它調用的時候,看似互相呼叫,一陰一陽,表面上看應該交替輸出@ 和 * , 但最終因為不斷返回到閉包的環境而輸出越來越多,且環境再疊加。

無論如何,再次謝謝大家很精彩的演繹,無論哪個語言,最後做到了 「相互呼叫」 和 「環境重返且疊加」 都值得大讚,很是精彩,直觀優雅! 謝謝!!

最後貼上從@bhuztez 那裡來的圖,和拷貝 @趙劼 實現的JS版本。

function MakeYin() {
return cc = function () {
console.log("@");

var yang = MakeYang(cc);
return yang(yang);
};
}
function MakeYang(yin){
return cc = function () {
console.log("*");
return yin(cc);
};
}
var yin = MakeYin();
yin(yin);


yin yang puzzle不過是

yin = cc()
echo "@"
yang = cc()
echo "*"
yin(yang)

無非就是cc會產生平行宇宙。這個用 pi-calculus 來表示比較好

proc yin_cc(O,A,B) {
new Yin in {
yin(O,A,B,Yin) | send Yin to Yin
}
}

proc yin(O,A,B,YinCC) {
recv Yin from YinCC;
send A to O;
{ yin(O,A,B,YinCC) | yang_cc(O,A,B,Yin) }
}

proc yang_cc(O,A,B,Yin) {
new Yang in {
yang(O,A,B,Yin,Yang) | send Yang to Yang
}
}

proc yang(O,A,B,Yin,YangCC) {
recv Yang from YangCC;
send B to O;
{ yang(O,A,B,Yin,YangCC) | send Yang to Yin }
}

proc output(O) {
recv X from O;
output(O)
}

proc main() {
new O, A, B in {
output(O) | yin_cc(O,A,B)
}
}

這樣就能直接看圖了 Diagram of π-Calculus

一看就會了,就是這麼簡單

----------------------------------------

我理解的執行過程是這樣,不知道為啥這麼理解和Ruby不一樣

1 2
- = cc() | -=1 | -=? | -=1 |
echo "@" @
+ = cc() | +=2 | | +=? |
echo "*" * 3
-(+) | | -=2 | | -=2 |
@
| +=3 | | +=? |
*
| +=3 |
* 4
| -=3 | | -=3 |
@
| +=4 | | +=? |
*
| +=4 |
*
| +=4 |
*

評論不方便貼代碼,直接貼這兒了

(let ((yin (call-with-current-continuation (lambda (c) c))))
(display #@)
(let ((yang (call-with-current-continuation (lambda (c) c))))
(display #*)
(yin yang)
)
)

Scheme明明沒問題嘛


我改寫的 Javascript 版,和 Scheme 版語句基本能一一對應的,已經非常便於理解了。

改寫的關鍵是使用了 CPS 風格,即所有函數沒有 return 語句,而參數列表的最後一個參數應該被傳入一個函數,通過該函數將返回值傳給下一級處理。

在 CPS 風格下,call/cc 的 Javascript 實現就很簡單了。Scheme 中的 call/cc 只需傳入了一個參數,實際上語言的內部實現會自動生成一個當時的 continuation 隱式的傳給它,這裡把這個過程變成顯式化的了。

在 NodeJS 下可直接運行。

var defer = function(f, context) {
return function() {
var args = arguments;
return process.nextTick(function() {
f.apply(context, args);
});
};
};

var callcc = function(lambda, cps) {
lambda(defer(cps), cps);
};

var yinyang = function() {
// yin"s call-with-current-continuation
callcc(
// (lambda (c) c)
function(c, cps) {
cps(c);
},
// yin"s countinuation
function(value) {
// (let ((yin) ((lambda (cc)
// (display #@) cc)
// (yin"s continuation))))
var yin = (function(cc) {
process.stdout.write("@");
return cc;
})(value);
// yang"s call-with-current-continuation
callcc(
// (lambda (c) c)
function(c, cps) {
cps(c);
},
// yang"s continuation
function(value) {
// (let ((yang) ((lambda (cc)
// (display #*) cc)
// (yang"s continuation))))
var yang = (function(cc) {
process.stdout.write("*");
return cc;
})(value);
// (yin yang)
yin(yang);
}
);
}
);
};

yinyang();


給個簡單的做法

不想做 CPS 變換又要實現 call/cc 的話,把要執行的代碼放數組裡就行了嘛(逃

一個小問題是,這樣的寫法在 JavaScript 里不能像 LISP 里那樣實現一層層嵌套的 let 綁定,所以要在函數參數里存一下

"use strict";

const callcc_yin = (func, t) =&> func((ret) =&> {
yin = ret;
task = t.slice(0);
});

const callcc_yang = (func, y, t) =&> func((ret) =&> {
yin = y;
yang = ret;
task = t.slice(0);
});

let yin, yang;
let task = [
() =&> callcc_yin((ret) =&> ret(ret), task),
() =&> process.stdout.write("@"),
() =&> callcc_yang((ret) =&> ret(ret), yin, task),
() =&> process.stdout.write("*"),
() =&> yin(yang),
];

while (true) {
task.shift()();
}

反正只有兩個 call/cc 函數會改寫 task 數組,我們可以把其它代碼合併進這兩個函數里

"use strict";

const callcc_yin = (func, t) =&> func((ret) =&> {
yin = ret;
task = t.slice(0);
process.stdout.write("@");
});

const callcc_yang = (func, y, t) =&> func((ret) =&> {
yang = ret;
yin = y;
task = t.slice(0);
process.stdout.write("*");
yin(yang);
});

let yin, yang;
let task = [
() =&> callcc_yin((ret) =&> ret(ret), task),
() =&> callcc_yang((ret) =&> ret(ret), yin, task),
];

while (true) {
task.shift()();
}

可以發現,task 數組作為 t 傳進去的時候,內容是固定的(就是當前位置之後的代碼),於是愉快地硬編碼進去

"use strict";

const callcc_yin = (func) =&> func((ret) =&> {
yin = ret;
task = [() =&> callcc_yang((ret) =&> ret(ret), yin)];
process.stdout.write("@");
});

const callcc_yang = (func, y) =&> func((ret) =&> {
yang = ret;
yin = y;
task = [];
process.stdout.write("*");
yin(yang);
});

let yin, yang;
let task = [() =&> callcc_yin((ret) =&> ret(ret))];

while (true) {
task.shift()();
}

那些暫存的變數也可以消去

"use strict";

const callcc_yin = (func) =&> func((yin) =&> {
task = [() =&> callcc_yang((ret) =&> ret(ret), yin)];
process.stdout.write("@");
});

const callcc_yang = (func, yin) =&> func((yang) =&> {
task = [];
process.stdout.write("*");
yin(yang);
});

let task = [() =&> callcc_yin((ret) =&> ret(ret))];

while (true) {
task.shift()();
}

最後,如果你不在意堆棧溢出的話,task 數組其實也可以不要了

"use strict";

const callcc_yin = (func) =&> func((yin) =&> {
process.stdout.write("@");
callcc_yang((ret) =&> ret(ret), yin);
});

const callcc_yang = (func, yin) =&> func((yang) =&> {
process.stdout.write("*");
yin(yang);
});

callcc_yin((ret) =&> ret(ret));


function output(x){
console.log(x);
}
function con(x,y,cont){ return cont(y); }
function yin(cont){
return (function(c,cont2){ return cont2(c); })(
function(v2,cont1){
return (function(cc,cont0){ return con(output(0),cc,cont0); })(v2,cont);
},
function(v2){
return (function(cc,cont0){ return con(output(0),cc,cont0); })(v2,cont);
}
);
}
function yang(cont){
return (function(c,cont2){ return cont2(c); })(
function(v2,cont1){
return (function(cc,cont0){ return con(output(1),cc,cont0); })(v2,cont);
},
function(v2){
return (function(cc,cont0){ return con(output(1),cc,cont0); })(v2,cont);
}
);
}
function f(cont){
return yin(
function(v1){
return yang(function(v2){ return v1(v2,cont); });
}
);
}
f(function(x){return x;});

JavaScript Yin Yang Puzzle

這個實現不是我人工寫的, 是由我的編譯器生成的, 原Yin Yang Puzzle的實現代碼如下

(lambda:con(x,y)=y)
(lambda:yin()=(lambda:cc-&>con(output(0),cc))(callcc(lambda:c-&>c)))
(lambda:yang()=(lambda:cc-&>con(output(1),cc))(callcc(lambda:c-&>c)))
(lambda:f()=yin()(yang()))

進行cps編譯變換後源對源編譯成JavaScript, 人工添加了output函數和對函數f進行調用.

上面實現Yin Yang Puzzle的語言和cps編譯變換可以看我的文章

CPS變換與CPS變換編譯

要生成JavaScript只需要把代碼貼進在線解釋器頁面的文本框然後在console里輸入

JavaScript_generator(CPS(parser(lexer(getTextContent($("#code_editor pre.code").get(0))))));


只有支持 call/cc 語言能搞這個魔法... Ruby 版:

require "continuation"

yin, yang =
callcc{|c| c }.tap{ print "@" },
callcc{|c| c }.tap{ print "*" }

yin.(yang)

解釋一下:

obj.tap { ... } 會執行 block 內容, 但最後返回 obj

callcc{|c| c } 相當於 Scheme 里的 (call/cc (lambda (c) c)) 無非就是返回當前 continuation 而已

其實 Scheme 能玩的, Ruby 里都會更簡單...

或者這麼寫:

require "continuation"

callcc{|x|x}.tap{ print "@" }.call
callcc{|x|x}.tap{ print "*" }

輸出也是 @*@**@***@****@*****@******@*******@********@*********@**********@***********

如果像下面這麼寫好像更簡單點?

yin = callcc{|x|x}
print "@"
yang = callcc{|x|x}
print "*"
yin.(yang)

但結果不對... 在 Scheme 里 let 會在棧上壓進去一個新變數, 不管之前有沒有定義同名變數, 所以每個 let 都創造了一個新的世界, 但 Ruby 的變數賦值其實相當於 set! 而不是 let. 為了達成 let 的效果, 可以用 lambda + apply 來實現.

知道了這一點, 我們就可以變換各種姿勢了...

這樣也行:

yin, _, yang, _ = callcc{|x|x}, print("@"), callcc{|x|x}, print("*")
yin.(yang)

這樣也行 (面向組合子編程...):

[callcc{|x|x}, print("@"), callcc{|x|x}, print("*")].compact.reduce :call

這樣也行 (lambda + apply 實現 let):

-&> yin {
print "@"
-&> yang {
print "*"
yin.(yang)
}.(callcc{|x|x})
}.(callcc{|x|x})

-------------- 1月22日更新

一開始我的理解不太對, let* 其實是翻譯成嵌套 let 的, 並行賦值只是碰巧最終效果和 let 一樣而已. 已經更新了上面的一些解釋.


// stack free version
var defer = function(f, context) {
return function() {
var args = arguments;
return setTimeout(function() {
f.apply(context, args);
});
};
};

// or simpler version 以下的講解按照簡單版本來
var defer = function(f, context) {
return function() {
var args = arguments;
f.apply(context, args);
};
};

var callcc = function(lambda, cps) {
lambda(defer(cps), cps);
};

var yinyang = function() {
// yin"s call-with-current-continuation
callcc(
// (lambda (c) c)
function(c, cps) {
cps(c);
},
// yin"s countinuation
function(value) {
// (let ((yin) ((lambda (cc)
// (display #@) cc)
// (yin"s continuation))))
var yin = (function(cc) {
console.log("@");
return cc;
})(value);
// yang"s call-with-current-continuation
callcc(
// (lambda (c) c)
function(c, cps) {
cps(c);
},
// yang"s continuation
function(value) {
// (let ((yang) ((lambda (cc)
// (display #*) cc)
// (yang"s continuation))))
var yang = (function(cc) {
console.log("*");
return cc;
})(value);
// (yin yang)
yin(yang);
}
);
}
);
};

yinyang();

這段JS代碼來自 @張護國,我只是把它改成了可以在瀏覽器中執行的代碼。

為什麼我這麼無聊呢?因為瀏覽器執行 JS 代碼可以方便的打斷點

下面來分析一下這段程序。

我們將從小的結構開始分析。

一、lcc

// (lambda (c) c)
var lcc = function(c, cps) {
// c 和 cps 都是函數
cps(c);
}

這個的lcc的作用是先執行c(對c求值),再執行 cps()

二、callcc

這個callcc的第一個參數lambda,其實就是lcc。

var callcc = function(lambda, cps) {
lambda(defer(cps), cps);
};

所以,每當執行 callcc() 的時候,derfer(cps)先被執行,然後是執行 cps()

// callcc(lcc, cps) is
var v = defer(cps);
cps(v);

強調一個細節,在lcc中,cps(c)的c就是defer(cps)返回值,也就是相當於 cps(defer(cps))

三、cps

然後看cps是什麼。

cps是陰陽謎題的body,比如yang,是

var yang_body = function(value) {
// (let ((yang) ((lambda (cc)
// (display #*) cc)
// (yang"s continuation))))
var yang = (function(cc) {
console.log("*");
return cc;
})(value);
// (yin yang)
yin(yang);
}

四、yin/yang

現在,我將換個角度,來看看yin和yang變數是在幹什麼。yin和yang的結構是一樣的,分析哪一個都可以。我們來分析yang。

var yang = (function(cc) {
console.log("*");
return cc;
})(value);

當我們初始化yang時,效果是這樣的

var yang;
console.log("*");
yang=value;

五、defer

defer 的作用如其名,延遲某個 function

var defer = function(f, context) {
return function() {
var args = arguments;
f.apply(context, args);
};
};

(針對stack free 版本:setTimeout不帶第二個參數,它的作用其實就是執行完畢當前的函數後,立刻執行這個函數。當然,規範上或許是undefined的行為,但是從設計引擎的角度講,這是最簡單的做法。萬幸,chrome上的行為和我們的猜測一致。)

所以defer(f) 就是f的另一種延遲表達。defer(cps)() 就是立即執行 cps()

六、要把一切連起來

事先說明,如果你在閱讀下面這些分析的時候有困難,請在yin和yang被賦值的那裡打下斷點。

當我們執行 yinyang() 時,執行的是 callcc(lcc,yin_body)

我們知道,這相當於

var value = defer(yin_body);
yin_body(value);

value 是defer(yin_body)的返回值,是一個function,這個function的作用是 will_execute_yin_body。注意,作用不是 execute_yin_body_now。

1. 第一輪

接下來,我們在yin_body裡面的第一個語句就是

var yin = (function(cc) {
console.log("@");
return cc;
})(value);

我們已經知道,這相當於

var yin;
console.log("@");
yin=value;

此時,列印了@,不信你可以打一下斷點試試看。

然後執行第二個callcc語句,我們知道,這個callcc語句相當於執行yang_body:

var value = defer(yang_body);
yang_body(value);

而yang_body中第一個語句相當於

var yang;
console.log("*");
yang=value;

此時列印了一個 * 星號,然後執行

yin(yang);

接下來,我們分析這個語句的執行效果,我們稱這個語句為第二輪。

2. 第二輪

執行yin(yang)就是執行

yin_body(yang);

會導致列印 @

參數是yang,說明裡面的value就是yang

也就是第一個語句會變成

var yin;
console.log("@");
yin=yang; // yang is defer(yang_body)

第一個語句之後,我們繼續執行yang_body的callcc

第一句相當於

var yang;
console.log("*");
yang=value; // defer(yang_body)

列印了 *,yang_body的最後一句是

yin(yang)

但是實際上變成了

yang_body(yang);

yang_body的第一句相當於

console.log("*");
yang = value; // defer(yang_body)

列印了 *,這是此輪第二次列印 *

然後繼續執行yin(yang),注意,此時的yin是閉包從第一輪的時候catch的,所以依然是原值 defer(yin_body)

繼續分析下去,我們就明白陰陽謎題會列印這個結果了。

待續。


let callcc=(func,context,continuation)=&>{
let contextCopy=Object.assign({},context)
return func((value)=&>{throw {value,contextCopy,continuation}})
}
let run=(program)=&>{
let context={}
let continuation=0
let callccreturn=undefined
while(true){
try{
program(context,continuation,callccreturn)
break;
}catch(e){
continuation=e.continuation
callccreturn=e.value
context=e.contextCopy
}
}
}
let program=(context,continuation,callccreturn)=&>{
let callccrt=callccreturn
switch(continuation){
case 0:
callccrt=callcc(i=&>i,context,1)
case 1:
context.yin=callccrt
console.log("@")
callccrt=callcc(i=&>i,context,2)
case 2:
context.yang=callccrt
console.log("*")
context.yin(context.yang)
default:
}
}
run(program)


手工將callcc展開後,化簡得到,下面這個函數然後 (A A)就可以了。 @Zete. 這個用ruby怎麼改。

(define (A a)
(display "
")
(display " *")
(a (lambda (aa)
(display " *")
(a aa))))


參考了 hczhcz 的代碼,然後用我寫的腳本語言bajdcc/jMiniLang去實現,jMiniLang支持閉包,所以實現程序也不在話下。

jMiniLang代碼如下:

import "sys.base";

var r = func ~(r) {
return call r(r);
};

var callcc_yang = func ~(f, yin) {
var _yang = func ~(yang) {
call g_print("*");
call yin(yang);
};
return call f(_yang);
};

var callcc_yin = func ~(f) {
var _yin = func ~(yin) {
call g_print("@");
call callcc_yang(r, yin);
};
return call f(_yin);
};

call callcc_yin(r);

然後運行它,結果爆棧了 TVT,我設置的腳本調用棧深是50。

結果是:@*@**@***@****@*****@******@**(爆棧!)

查看了下調用棧:

代碼頁:extern,地址:0,名稱:extern,參數:[],變數:[{}],閉包:{},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC})rC}],閉包:{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC})rC}],閉包:{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC})rC}],閉包:{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC},
代碼頁:yinyang,地址:78,名稱:r,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC})rC],變數:[{}, {0=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC})rC}],閉包:{},
代碼頁:yinyang,地址:108,名稱:callcc_yang,參數:[函數(yinyang,78)rC, 函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC],變數:[{4=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC})rC}, {2=函數(yinyang,78)rC, 3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC}],閉包:{},
代碼頁:yinyang,地址:273,名稱:_yin,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC],變數:[{}, {3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC}],閉包:{0=函數(yinyang,78)rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC}],閉包:{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC}],閉包:{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC}],閉包:{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC}],閉包:{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC}],閉包:{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC}],閉包:{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC},
代碼頁:yinyang,地址:78,名稱:r,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC],變數:[{}, {0=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC}],閉包:{},
代碼頁:yinyang,地址:108,名稱:callcc_yang,參數:[函數(yinyang,78)rC, 函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC],變數:[{4=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC})rC}, {2=函數(yinyang,78)rC, 3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC}],閉包:{},
代碼頁:yinyang,地址:273,名稱:_yin,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC],變數:[{}, {3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC}],閉包:{0=函數(yinyang,78)rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC}],閉包:{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC}],閉包:{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC}],閉包:{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC}],閉包:{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC}],閉包:{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC},
代碼頁:yinyang,地址:78,名稱:r,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC],變數:[{}, {0=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC}],閉包:{},
代碼頁:yinyang,地址:108,名稱:callcc_yang,參數:[函數(yinyang,78)rC, 函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC],變數:[{4=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC})rC}, {2=函數(yinyang,78)rC, 3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC}],閉包:{},
代碼頁:yinyang,地址:273,名稱:_yin,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC],變數:[{}, {3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC}],閉包:{0=函數(yinyang,78)rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC}],閉包:{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC}],閉包:{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC}],閉包:{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC}],閉包:{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC},
代碼頁:yinyang,地址:78,名稱:r,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC],變數:[{}, {0=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC}],閉包:{},
代碼頁:yinyang,地址:108,名稱:callcc_yang,參數:[函數(yinyang,78)rC, 函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC],變數:[{4=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC})rC}, {2=函數(yinyang,78)rC, 3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC}],閉包:{},
代碼頁:yinyang,地址:273,名稱:_yin,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC],變數:[{}, {3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC}],閉包:{0=函數(yinyang,78)rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC}],閉包:{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC}],閉包:{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC}],閉包:{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC},
代碼頁:yinyang,地址:78,名稱:r,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC],變數:[{}, {0=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC}],閉包:{},
代碼頁:yinyang,地址:108,名稱:callcc_yang,參數:[函數(yinyang,78)rC, 函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC],變數:[{4=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC})rC}, {2=函數(yinyang,78)rC, 3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC}],閉包:{},
代碼頁:yinyang,地址:273,名稱:_yin,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC],變數:[{}, {3=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC}],閉包:{0=函數(yinyang,78)rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC}],閉包:{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC}],閉包:{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC},
代碼頁:yinyang,地址:78,名稱:r,參數:[函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC],變數:[{}, {0=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC}],閉包:{},
代碼頁:yinyang,地址:108,名稱:callcc_yang,參數:[函數(yinyang,78)rC, 函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC],變數:[{4=函數(yinyang,173,{3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC})rC}, {2=函數(yinyang,78)rC, 3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC}],閉包:{},
代碼頁:yinyang,地址:273,名稱:_yin,參數:[函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC],變數:[{}, {3=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC}],閉包:{0=函數(yinyang,78)rC},
代碼頁:yinyang,地址:173,名稱:_yang,參數:[函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC],變數:[{}, {5=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC}],閉包:{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC},
代碼頁:yinyang,地址:78,名稱:r,參數:[函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC],變數:[{}, {0=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC}],閉包:{},
代碼頁:yinyang,地址:108,名稱:callcc_yang,參數:[函數(yinyang,78)rC, 函數(yinyang,273,{0=函數(yinyang,78)rC})rC],變數:[{4=函數(yinyang,173,{3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC})rC}, {2=函數(yinyang,78)rC, 3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC}],閉包:{},
代碼頁:yinyang,地址:273,名稱:_yin,參數:[函數(yinyang,273,{0=函數(yinyang,78)rC})rC],變數:[{}, {3=函數(yinyang,273,{0=函數(yinyang,78)rC})rC}],閉包:{0=函數(yinyang,78)rC},
代碼頁:yinyang,地址:78,名稱:r,參數:[函數(yinyang,273,{0=函數(yinyang,78)rC})rC],變數:[{}, {0=函數(yinyang,273,{0=函數(yinyang,78)rC})rC}],閉包:{},
代碼頁:yinyang,地址:220,名稱:callcc_yin,參數:[函數(yinyang,78)rC],變數:[{7=函數(yinyang,273,{0=函數(yinyang,78)rC})rC}, {2=函數(yinyang,78)rC}],閉包:{},
代碼頁:yinyang,地址:10,名稱:main,參數:[],變數:[{0=函數(yinyang,78)rC, 1=函數(yinyang,108)rC, 6=函數(yinyang,220)rC}, {}],閉包:{},
代碼頁:yinyang,地址:0,名稱:null,參數:[],變數:[{}],閉包:{}]

調用非常清晰啊~什麼continuation,都包括在上述的閉包裡面。

同樣使用閉包的 Y-Combinator ,jMiniLang也可以實現:

不過如何避免不棧爆,就要用到Trampoline了, 它和Y-Combinator在 https://github.com/bajdcc/jMiniLang/blob/master/src/priv/bajdcc/LALR1/interpret/test/TestInterpret7.java 中。這得讓我好好想想。


用原始的java實現了一個版本,只能說是模擬。用java的對象模擬scheme的閉包,對象的屬性表模擬scheme的詞法環境,對象的方法模擬continuation再入點。

這裡沒有使用任何高階函數,但是手工做了變換的工作。主要的問題是構造函數是個很討厭的存在。

abstract class Closure

{

public abstract void run(Closure x);

}

class Yin extends Closure

{

Closure yin,yang;

public Yin() {

System.out.print("@");

}

public void run(Closure x) {

System.out.print("@");

yin = x;

yang = new Yang(yin);

yin.run(yang);

}

}

class Yang extends Closure

{

Closure yin,yang;

public Yang(Closure y) {

yin = y;

System.out.print("*");

}

public void run(Closure x) {

yang = x;

System.out.print("*");

yin.run(yang);

}

}

public class Main {

public static void main(String [] args) {

Yin yin = new Yin();

Yang yang = new Yang(yin);

yin.run(yang);

}

}


直譯:

delegate YinYang YinYang(YinYang cont);

static YinYang MakeYin()
{
return cout =&>
{
Console.Write("@");

var yang = MakeYang(cout);
return yang(yang);
};
}

static YinYang MakeYang(YinYang yin)
{
return cout =&>
{
Console.Write("*");
return yin(cout);
};
}

static void Main()
{
var yin = MakeYin();
yin(yin);
}

然後就一直運行到Stack Overflow了。


蟹妖,不過臣妾做不到啊,看不明白lisp啊……

難道是

// JavaScript
function yin(cc) {
(function (c) {
process.stdout.write("@");
c(yin);
})(cc);
}
function yang(cc) {
(function (c) {
process.stdout.write("*");
c(yang);
})(cc);
}
yin(yang);

輸出是

@*@*@*@*@*@*@*@*@*@*@*@*@*@*@*@*@*@*.....

然後瞬間

RangeError: Maximum call stack size exceeded

我胡謅的,我真的看不懂lisp,捂臉逃


(let ([f
(lambda (g)
(write-char #@)
(let ([h (lambda (x) (write-char #*) (g x))])
(h h)))])
(f f))

經過n次簡化後的陰陽謎題

============================================================================

1.將陰陽謎題cps化:

經過研究,我發現幾乎任何一個call/cc都可以轉換為等價的cps。所以這裡先將它cps化

代碼:

((lambda (foo k) (display #@) (k foo))
(call/cc (lambda (bar) bar))
(lambda (yin)
(lambda (foo k) (display #*) (k foo))
(call/cc (lambda (bar) bar))
(lambda (yang) (yin yang))))

然後,發現後面的(lambda (yang) (yin yang))其實等價於yin

於是:

((lambda (foo k) (display #@) (k foo))
(call/cc (lambda (bar) bar))
(lambda (yin)
(lambda (foo k) (display #*) (k foo))
(call/cc (lambda (bar) bar))
yin))

2.簡化代碼:

代碼:

(define f
(lambda (yin)
(display #@)
((lambda (foo k) (display #*) (k foo))
(call/cc (lambda (bar) bar))
yin)))
((lambda (foo k) (k foo))
(call/cc (lambda (bar) bar))
f)

3.移除call/cc

然後我發現f代表拿到continuation再display之後的continuation。於是把(call/cc ..)改為f,在將

(display #@)移到f的函數體里

代碼:

(define f
(lambda (yin)
(display #@)
((lambda (foo k) (display #*) (k foo))
(call/cc (lambda (bar) bar))
yin)))
((lambda (foo k) (k foo))
f
f)

同樣,下一個call/cc也可以消除

代碼:

(define f
(lambda (yin)
(display #@)
(let ([g (lambda (x) (display #*) (yin x))])
((lambda (foo k) (k foo))
g
g))))
((lambda (foo k) (k foo))
f
f)

4.進一步簡化:

((lambda (foo k) (k foo)) A B)可以簡化為(B A)

用此方法,即可簡化代碼

代碼:

(define f
(lambda (yin)
(display #@)
(let ([g (lambda (x) (display #*) (yin x))])
(g g))))
(f f)

再將define改為let:

(let ([f
(lambda (g)
(write-char #@)
(let ([h (lambda (x) (write-char #*) (g x))])
(h h)))])
(f f))

完工


class Yin
{
private event Action OnCall;
readonly Yan _yan = new Yan();
public void DoCall()
{
Console.Write("@");
OnCall += _yan.DoCall;
OnCall?.Invoke();
DoCall();
}
}

class Yan
{
public void DoCall()
{
Console.Write("*");
}
}

class Program
{
static void Main(string[] args)
{
Yin yin = new Yin();
yin.DoCall();
}
}

c# 版的,由於尾遞歸優化.不會爆棧.


看到Cpp1z新的[*this]特性...我就想這個能不能用來做call/cc

姑且寫了一個野指針亂飛的代碼...

測試在這裡 [Wandbox]三へ( へ?? ?)へ ???? 請dalao們指正..

( 然而我還不知道這是不是call/cc...不懂FP )

(反正模板元能實現λ-Calculus, 詳見Matt Might/灼弦巨巨的TemplatePL)

(我很懷疑這代碼有不止一處的UB...)

#include &
#include &
#include &

struct ControlFlow {
template &
auto callcc(F f) {
return f([*this](){ return *this; });
}

struct Yin {
void display() {
std::cout &<&< "@"; } struct id { template &
T operator()(T t) {
return t;
}
};
auto yin_callcc() {
return pcf-&>callcc(id{});
}
void new_and_copy() {
auto cf_lambda = yin_callcc();
ControlFlow* pcf2 = new ControlFlow;
auto ccf = cf_lambda();
std::memcpy(pcf2, ccf, sizeof(ccf));
qpcf.push(pcf2);
}
void call() {
display();
new_and_copy();
}
template &
void call(T* other) {
auto pcf2 = qpcf.front();
qpcf.pop();
*pcf = *pcf2;
qpcf.push(other-&>qpcf.front());
pcf-&>seq -= 1;
}
ControlFlow* pcf;
std::queue& qpcf;
};

struct Yang {
void display() {
std::cout &<&< "*"; } struct id { template &
T operator()(T t) {
return t;
}
};
auto yang_callcc() {
return pcf-&>callcc(id{});
}
void new_and_copy() {
auto cf_lambda = yang_callcc();
ControlFlow* pcf2 = new ControlFlow;
auto ccf = cf_lambda();
std::memcpy(pcf2, ccf, sizeof(ccf));
qpcf.push(pcf2);
}
void call() {
display();
new_and_copy();
}
template &
void call(T* other) {
auto pcf2 = qpcf.front();
qpcf.pop();
*pcf = *pcf2;
qpcf.push(other-&>qpcf.front());
pcf-&>seq -= 1;
}
ControlFlow* pcf;
std::queue& qpcf;
};
void* yin;
void* yang;
int seq;
ControlFlow() {
yin = new Yin();
yang = new Yang();
reinterpret_cast&(yin)-&>pcf = this;
reinterpret_cast&(yang)-&>pcf = this;
seq = 0;
}
void call() {
switch(seq) {
case 0:
reinterpret_cast&(yin)-&>call();
++seq;
break;
case 1:
reinterpret_cast&(yang)-&>call();
++seq;
break;
case 2:
reinterpret_cast&(yin)-&>call(reinterpret_cast&(yang));
++seq;
break;
default:
break;
}
}

};

int main() {
ControlFlow cf;
while(true) {
cf.call();
}
}


function yin(cc) {
setTimeout(function() {
(function(c) {
process.stdout.write("@");
c(yin);
})(cc);
});
}

function yang(cc) {
setTimeout(function() {
(function(c) {
process.stdout.write("*");
c(yang);
})(cc);
});
}
yin(yang);

JS 無Stackover非同步版,我只是把 @Jim Liu的改成非同步而已(捂臉逃


推薦閱讀:

是否Future/Promise模式 能實現的FRP都能更好的實現?
如何將MATLAB轉化為C#?
如何看待2017的 The .NET Language Strategy 去掉了C++/CLI支持?
什麼情況下使用異常處理?
C# 中如何有效地釋放內存?

TAG:編程 | C | 函數式編程 | C# |