我的背景是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语句有什么作用?
 main中的p是否将堆栈帧存储在堆中以及停止它的指令行中?
 跳回函数意味着再次创建堆栈框架并从我们离开的地方继续?

在此代码下方有一段


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

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

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


堆栈被破坏了什么,他的意思是如果使用goto可能会发生堆栈溢出? callCC如何使用蹦床解决此问题?

许多人说callCC提供了Early Return语义,这意味着它像是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就像需要将来执行的预定义代码? (我是在讨论框架和用户代码的角度)

最佳答案

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


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


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


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


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


不,这意味着一次跳转,仅此而已-无需堆栈操作。控制流跳到标有L的行,但没有其他更改。堆栈保持不变。


  堆栈被破坏了什么,他的意思是如果使用goto可能会发生堆栈溢出?


实际上是下溢。调用label_as_result时,会将一帧压入堆栈。返回时会弹出该框架。然后我们跳转到L,执行printf,然后到达函数的结尾,这将再次弹出堆栈。因此,最后,堆栈弹出的次数比推送堆栈的次数多。


  callCC如何解决这个问题


通过实际执行您假定的C代码正在执行的操作:保存和还原堆栈,而不仅仅是在保持堆栈相同的情况下跳转到代码行。


  许多人说callCC提供了Early Return语义,这意味着它像是python还是Javascript中的yield?


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


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


不,但是可以使用yield在Scheme中写入callCCcallCC严格来说是两者中最强大的。


  我如何用JavaScript构思以上代码


实际的C代码不是您可以在JavaScript中复制的东西(除了通过使用自己的堆栈构建微型VM之外),因为JavaScript不允许您破坏这样的堆栈。

如您在第二个代码示例中所做的那样,可以通过从label_as_result返回一个函数来实现不会破坏堆栈的不间断版本。


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


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


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


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


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


不太清楚您的意思,但这听起来不对。 callCC是使您可以访问当前延续的功能。

关于javascript - CallCC是goto的改进版本吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59234399/

10-11 23:20