Scheme 阴阳之谜是如何工作的?

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

我试图掌握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 (lambda (c) c)))) )
    (yin yang))
它应该输出
@*@***@****@…
, 但我不明白为什么;我希望它能输出
@*@*********


有人能详细解释一下为什么阴阳之谜是这样运作的吗

我不认为我完全理解这一点,但对此我只能想出一个解释(极其夸张):

  • let*
    中的
    yin
    yang
    第一次绑定时,打印第一个@和*
    (阴阳)
    ,在第一次呼叫/cc完成后,它返回顶部
  • 打印下一个@和*,然后打印另一个*,因为这一次,
    yin
    被重新绑定到第二个调用/cc的值
  • (yin-yang)
    再次应用,但这次它在原始
    yang
    的环境中执行,其中
    yin
    绑定到第一个调用/cc,因此控制返回到打印另一个@。
    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 ; @ * * *
...