0

我的背景是 Javascript、Python 和一点 Haskell。我正在尝试理解 callCC,有很多解释,但我找不到它们微不足道的 & 我遇到了这个https://www.cs.bham.ac.uk/~hxt/research/Logiccolumn8.pdf我觉得我几乎明白了,但我需要一些帮助来理解 C 代码。

下面是在 GNU C 中跳回函数的代码。

void *label_as_result() {
     return &&L;
  L: printf("Jumped back into the function. \n");
}

main () {
    void *p;
    p = label_as_result();
    printf("The function returned; now jump back into it.\n");
    goto *p;
}

label_as_result函数中的 return 语句有什么作用?p in main是否将堆栈帧存储在堆中以及它停止的指令行中?跳回函数意味着再次创建一个堆栈帧并从我们离开的地方继续?

在这段代码下面有一段

但是在具有一流函数和callCC的语言中,没有这样的实现限制。就像C中的结果标签一样,我们可以从函数返回由callCC引入的延续,以便跳回函数。当我们使用goto执行此操作时,堆栈被粉碎,但使用 callCC 函数再次返回。考虑以下函数

 λ(). callCC(λk. λx. throw k (λy. x))

延续 k 作为结果的一部分返回,大致类似于在 C 中返回标签作为结果。

堆栈被砸了是什么意思,他的意思是如果你使用 goto 会发生堆栈溢出?callCC 如何使用蹦床解决这个问题?

正如许多人所说,callCC 提供了早期返回语义,这意味着它像 python 或 Javascript 中的 yield 吗?是否可以使用yield在Javascript中编写callCC?

我如何在 Javascript 中构思上述代码

function* label_as_result() {
    yield () => console.log("Jumped back into the function.");
}

p = label_as_result().next().value;
console.log("The function returned; now jump back into it.");
p();

它甚至可以在没有任何生成器概念的情况下简单地编写为

function label_as_result() {
    return () => console.log("Jumped back into the function.");
}

p = label_as_result();
console.log("The function returned; now jump back into it.");
p();

这意味着 callCC 是一个返回延续但所有其他函数都采用延续的函数。延续就像未定的代码,需要在未来执行,而 callCC 就像预定义的代码,需要在未来执行?(我说的是框架和用户代码)

4

1 回答 1

3

label_as_result 函数中的 return 语句有什么作用?

它返回标记为 的指令的地址L。也就是说,它返回编译器生成的代码printf("Jumped back into the function. \n");存储在内存中的地址。

p in main 是否将堆栈帧存储在堆中以及它停止的指令行中?

不,它存储L标签所在的指令行。这就是它存储的所有内容。

跳回函数意味着再次创建一个堆栈帧并从我们离开的地方继续?

不,这意味着单次跳转,仅此而已 - 没有堆栈操作。控制流跳转到标记为 的行L,但没有其他任何变化。堆栈保持不变。

堆栈被砸了是什么意思,他的意思是如果你使用 goto 会发生堆栈溢出?

实际上,下溢。当label_as_result被调用时,一个帧被推入堆栈。当它返回时,该帧被弹出。然后我们跳转到L,执行printf,然后到达函数的末尾,这将再次弹出堆栈。所以最终堆栈被弹出的次数比被压入的次数要多。

callCC 如何解决这个问题

通过实际执行您假设 C 代码正在执行的操作:保存和恢复堆栈,而不是在保持堆栈不变的同时跳转到代码行。

正如许多人所说,callCC 提供了早期返回语义,这意味着它像 python 或 Javascript 中的 yield 吗?

它们在某种意义上是相似的,它们都为您提供了一种早期回报,并且它们可以用于一些相同的事情。您可以将其yield视为一种更专业的工具,旨在提供一种更简单的方法来实现callCC.

是否可以使用yield在Javascript中编写callCC?

不,但可以yield使用callCC. callCC严格来说是两者中更强大的。

我如何在 Javascript 中构思上述代码

实际的 C 代码不是您可以在 JavaScript 中复制的东西(除了通过构建具有自己的堆栈的 mini-VM 之外),因为 JavaScript 不允许您像那样破坏堆栈。

一个不破坏堆栈的完整版本可以通过返回一个函数来完成,label_as_result就像您在第二个代码示例中所做的那样。

这意味着 callCC 是一个返回延续的函数

callCC是一个使用当前延续调用另一个函数的函数。这可以用来返回延续。

延续就像未定的代码,需要在未来执行

当然,除了我不会在这里说“需要”。您不必调用延续(或者您甚至可以多次调用它)

但是 callCC 就像预定义的代码,需要在 Future 中执行?

不太清楚你在这里的意思,但这听起来不对。callCC是一个功能,可让您访问当前的延续。

于 2019-12-08T11:13:39.387 回答