Scheme 阴阳之谜是如何工作的?
我试图掌握Scheme中call/cc的语义,Wikipedia页面上的continuations以阴阳之谜为例:Scheme 阴阳之谜是如何工作的?,scheme,callcc,Scheme,Callcc,我试图掌握Scheme中call/cc的语义,Wikipedia页面上的continuations以阴阳之谜为例: (let* ((yin ((lambda (cc) (display #\@) cc) (call-with-current-continuation (lambda (c) c)))) (yang ((lambda (cc) (display #\*) cc) (call-with-current-continuation (la
(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))
它应该输出@*@***@****@…
,
但我不明白为什么;我希望它能输出@*@*********
有人能详细解释一下为什么阴阳之谜是这样运作的吗 我不认为我完全理解这一点,但对此我只能想出一个解释(极其夸张):
- 当
中的let*
和yin
第一次绑定时,打印第一个@和*yang
,在第一次呼叫/cc完成后,它返回顶部(阴阳)
- 打印下一个@和*,然后打印另一个*,因为这一次,
被重新绑定到第二个调用/cc的值李>yin
再次应用,但这次它在原始(yin-yang)
的环境中执行,其中yang
绑定到第一个调用/cc,因此控制返回到打印另一个@。yin
参数包含在第二次传递时重新捕获的延续,正如我们已经看到的,它将导致打印yang
。所以在这第三遍中,**
将被打印,然后调用这个双星打印延续,因此它以三星结束,然后这个三星延续被重新捕获李>@*
; call (yin yang)
(define (yy yin yang) (yin yang))
; run (call-yy) to set it off
(define (call-yy)
(yy
( (lambda (cc) (display #\@) cc) (call/cc (lambda (c) c)) )
( (lambda (cc) (display #\*) cc) (call/cc (lambda (c) c)) )
)
)
(define (ccc2) (call/cc (lambda (c) c)) )
(define (call-yy2)
(
( (lambda (cc) (display #\@) cc) (ccc2) )
( (lambda (cc) (display #\*) cc) (ccc2) )
)
)
(let* ((yin
(f ###))
(yang
(g (call-with-current-continuation id))))
(yin yang))
yin = f C_n ; @
yang = g #C_{n+1}#
yin yang
或者使用一些额外的显示语句来帮助查看发生了什么:
; create current continuation and tell us when you do
(define (ccc)
(display "call/cc=")
(call-with-current-continuation (lambda (c) (display c) (newline) c))
)
; call (yin yang)
(define (yy yin yang) (yin yang))
; run (call-yy) to set it off
(define (call-yy)
(yy
( (lambda (cc) (display "yin : ") (display #\@) (display cc) (newline) cc)
(ccc) )
( (lambda (cc) (display "yang : ") (display #\*) (display cc) (newline) cc)
(ccc) )
)
)
或者像这样:
; call (yin yang)
(define (yy yin yang) (yin yang))
; run (call-yy) to set it off
(define (call-yy)
(yy
( (lambda (cc) (display #\@) cc) (call/cc (lambda (c) c)) )
( (lambda (cc) (display #\*) cc) (call/cc (lambda (c) c)) )
)
)
(define (ccc2) (call/cc (lambda (c) c)) )
(define (call-yy2)
(
( (lambda (cc) (display #\@) cc) (ccc2) )
( (lambda (cc) (display #\*) cc) (ccc2) )
)
)
(let* ((yin
(f ###))
(yang
(g (call-with-current-continuation id))))
(yin yang))
yin = f C_n ; @
yang = g #C_{n+1}#
yin yang
可能的答案
这可能不对,但我要试一试
我认为关键的一点是,一个“被调用”的延续将堆栈返回到以前的某个状态——就好像没有发生其他任何事情一样。当然,它不知道我们通过显示@
和*
字符来监视它
我们最初将yin
定义为一个延续a
,它将实现以下功能:
1. restore the stack to some previous point
2. display @
3. assign a continuation to yin
4. compute a continuation X, display * and assign X to yang
5. evaluate yin with the continuation value of yang - (yin yang)
但如果我们称之为yangcontinuation,则会发生以下情况:
1. restore the stack to some point where yin was defined
2. display *
3. assign a continuation to yang
4. evaluate yin with the continuation value of yang - (yin yang)
我们从这里开始
当yin
和yang
被初始化时,第一次通过你得到yin=A
和yang
The output is @*
(计算A
和B
连续性。)
现在(阴阳)
第一次被评估为(A B)
我们知道A
做什么。它是这样做的:
1. restores the stack - back to the point where yin and yang were being initialised.
2. display @
3. assign a continuation to yin - this time, it is B, we don't compute it.
4. compute another continuation B', display * and assign B' to yang
The output is now @*@*
5. evaluate yin (B) with the continuation value of yang (B')
1. restore the stack - back to the point where yin was already initialised.
2. display *
3. assign a continuation to yang - this time, it is B'
The output is now @*@**
4. evaluate yin with the continuation value of yang (B')
1. restores the stack - back to the point where yin and yang were being initialised.
2. display @
3. assign a continuation to yin - this time, it is B', we don't compute it.
4. compute another continuation B", display * and assign B" to yang
The output is now @*@**@*
5. evaluate yin (B') with the continuation value of yang (B")
1. restore the stack - back to the point where yin=B.
2. display *
3. assign a continuation to yang - this time, it is B"
The output is now @*@**@**
4. evaluate yin (B) with the continuation value of yang (B")
1. restore the stack - back to the point where yin=A and yang were being initialised.
2. display *
3. assign a continuation to yang - this time, it is B'"
The output is now @*@**@***
4. evaluate yin with the continuation value of yang (B'")
现在(阴阳)
被评估为(B')
我们知道B
做什么。它是这样做的:
1. restores the stack - back to the point where yin and yang were being initialised.
2. display @
3. assign a continuation to yin - this time, it is B, we don't compute it.
4. compute another continuation B', display * and assign B' to yang
The output is now @*@*
5. evaluate yin (B) with the continuation value of yang (B')
1. restore the stack - back to the point where yin was already initialised.
2. display *
3. assign a continuation to yang - this time, it is B'
The output is now @*@**
4. evaluate yin with the continuation value of yang (B')
1. restores the stack - back to the point where yin and yang were being initialised.
2. display @
3. assign a continuation to yin - this time, it is B', we don't compute it.
4. compute another continuation B", display * and assign B" to yang
The output is now @*@**@*
5. evaluate yin (B') with the continuation value of yang (B")
1. restore the stack - back to the point where yin=B.
2. display *
3. assign a continuation to yang - this time, it is B"
The output is now @*@**@**
4. evaluate yin (B) with the continuation value of yang (B")
1. restore the stack - back to the point where yin=A and yang were being initialised.
2. display *
3. assign a continuation to yang - this time, it is B'"
The output is now @*@**@***
4. evaluate yin with the continuation value of yang (B'")
由于堆栈被恢复到yin=A
,因此(yin-yang)
被评估为(ab')
我们知道A
做什么。它是这样做的:
1. restores the stack - back to the point where yin and yang were being initialised.
2. display @
3. assign a continuation to yin - this time, it is B, we don't compute it.
4. compute another continuation B', display * and assign B' to yang
The output is now @*@*
5. evaluate yin (B) with the continuation value of yang (B')
1. restore the stack - back to the point where yin was already initialised.
2. display *
3. assign a continuation to yang - this time, it is B'
The output is now @*@**
4. evaluate yin with the continuation value of yang (B')
1. restores the stack - back to the point where yin and yang were being initialised.
2. display @
3. assign a continuation to yin - this time, it is B', we don't compute it.
4. compute another continuation B", display * and assign B" to yang
The output is now @*@**@*
5. evaluate yin (B') with the continuation value of yang (B")
1. restore the stack - back to the point where yin=B.
2. display *
3. assign a continuation to yang - this time, it is B"
The output is now @*@**@**
4. evaluate yin (B) with the continuation value of yang (B")
1. restore the stack - back to the point where yin=A and yang were being initialised.
2. display *
3. assign a continuation to yang - this time, it is B'"
The output is now @*@**@***
4. evaluate yin with the continuation value of yang (B'")
我们知道B'
做什么。它是这样做的:
1. restores the stack - back to the point where yin and yang were being initialised.
2. display @
3. assign a continuation to yin - this time, it is B, we don't compute it.
4. compute another continuation B', display * and assign B' to yang
The output is now @*@*
5. evaluate yin (B) with the continuation value of yang (B')
1. restore the stack - back to the point where yin was already initialised.
2. display *
3. assign a continuation to yang - this time, it is B'
The output is now @*@**
4. evaluate yin with the continuation value of yang (B')
1. restores the stack - back to the point where yin and yang were being initialised.
2. display @
3. assign a continuation to yin - this time, it is B', we don't compute it.
4. compute another continuation B", display * and assign B" to yang
The output is now @*@**@*
5. evaluate yin (B') with the continuation value of yang (B")
1. restore the stack - back to the point where yin=B.
2. display *
3. assign a continuation to yang - this time, it is B"
The output is now @*@**@**
4. evaluate yin (B) with the continuation value of yang (B")
1. restore the stack - back to the point where yin=A and yang were being initialised.
2. display *
3. assign a continuation to yang - this time, it is B'"
The output is now @*@**@***
4. evaluate yin with the continuation value of yang (B'")
现在(阴阳)
被评估为(B”)
我们知道B
的作用。它的作用是:
1. restores the stack - back to the point where yin and yang were being initialised.
2. display @
3. assign a continuation to yin - this time, it is B, we don't compute it.
4. compute another continuation B', display * and assign B' to yang
The output is now @*@*
5. evaluate yin (B) with the continuation value of yang (B')
1. restore the stack - back to the point where yin was already initialised.
2. display *
3. assign a continuation to yang - this time, it is B'
The output is now @*@**
4. evaluate yin with the continuation value of yang (B')
1. restores the stack - back to the point where yin and yang were being initialised.
2. display @
3. assign a continuation to yin - this time, it is B', we don't compute it.
4. compute another continuation B", display * and assign B" to yang
The output is now @*@**@*
5. evaluate yin (B') with the continuation value of yang (B")
1. restore the stack - back to the point where yin=B.
2. display *
3. assign a continuation to yang - this time, it is B"
The output is now @*@**@**
4. evaluate yin (B) with the continuation value of yang (B")
1. restore the stack - back to the point where yin=A and yang were being initialised.
2. display *
3. assign a continuation to yang - this time, it is B'"
The output is now @*@**@***
4. evaluate yin with the continuation value of yang (B'")
由于堆栈被恢复到yin=A
,(阴阳)
被评估为(A B')
我想我们现在有了一个模式
每次我们调用(阴阳)
时,我们会循环一堆B
延续,直到回到yin=a
时,我们显示@
。我们在B
continuations的堆栈中循环,每次编写*
(如果这大致正确,我会非常高兴!)
谢谢你的提问。了解计划
我认为理解这个谜题至少有一半的问题是Scheme语法,这是大多数人不熟悉的
首先,我个人认为,call/cc x
比同等的替代方案,x get/cc
更难理解。它仍然称为x,将其作为当前的延续,但不知何故,它更适合在我的大脑回路中表现出来
考虑到这一点,构造(使用当前延续调用(lambda(c)c))
变得简单get cc
。我们现在要做的是:
(let* ((yin
((lambda (cc) (display #\@) cc) get-cc))
(yang
((lambda (cc) (display #\*) cc) get-cc)) )
(yin yang))
下一步是内部lambda的主体(display#\@)cc
,在更熟悉的语法中(无论如何,对我来说)意味着print@;返回cc代码>。在编写过程中,让我们将lambda(cc)body
重写为function(arg){body}
,删除一组括号,并将函数调用更改为类似C的语法,以获得以下结果:
(让*yin=
(函数(arg){print@;return arg;})(get cc)
杨=
(函数(arg){print*;return arg;})(get cc)
阴(阳)
现在开始变得更有意义了。现在,将其完全重写为类似C的语法(或者类似JavaScript的语法,如果您愿意的话)只需一小步,就可以得到:
var阴、阳;
yin=(函数(arg){print@;return arg;})(get cc);
yang=(函数(arg){print*;return arg;})(get cc);
阴(阳);
最困难的部分现在结束了,我们已经从这个方案中解码了!只是开玩笑;这很难,因为我以前没有这个计划的经验。所以,让我们来弄清楚这到底是怎么回事
连续体入门
观察奇怪的阴阳核心:它定义了一个函数,然后立即调用它。它看起来就像(函数(a,b){returna+b;})(2,3)
,可以简化为5
。但是简化阴/阳内部的调用将是一个错误,因为我们没有传递一个普通的值。我们将函数传递为一个延续
一眼望去,续集是一种奇怪的野兽。考虑更简单的程序:
var x=get cc;
打印x;
x(5);
最初,x
被设置为当前的延续对象(请放心,print x(let* ((yin C_1)
(yang
(g ###)))
(yin yang))
(let* ((yin C_1)
(yang C_2))
(yin yang))
(C_1 C_2)
(let* ((yin C_0)
(yang
(g C_2)))
(yin yang))
(C_0 C_2)
(let* ((yin C_2)
(yang
(g ###)))
(yin yang))
(C_2 C_3)
yin = f cc id
yang = g cc id
yin yang
---
yin = f #C_0# ; @
yang = g cc id
yin yang
---
yin = C_0
yang = g #C_1# ; *
yin yang
---
C_0 C_1
---
yin = f C_1 ; @
yang = g #C_2# ; *
yin yang
---
C_1 C_2
---
yin = C_0
yang = g C_2 ; *
yin yang
---
C_0 C_2
---
yin = f C_2 ; @
yang = g #C_3#; *
yin yang
---
C_2 C_3
---
yin = C_1
yang = g C_3 ; *
yin yang
---
C_1 C_3
---
yin = C_0
yang = g C_3 ; *
yin yang
---
C_0 C_3
yin = f C_n ; @
yang = g #C_{n+1}#
yin yang
yin = C_{n-1}
yang = g C_{n+1} ; *
yin yang
C_0 C_1 ; @ *
C_[1-0] C_2 ; @ * *
C_[2-0] C_3 ; @ * * *
...