我的背景是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中写入callCC
。 callCC
严格来说是两者中最强大的。
我如何用JavaScript构思以上代码
实际的C代码不是您可以在JavaScript中复制的东西(除了通过使用自己的堆栈构建微型VM之外),因为JavaScript不允许您破坏这样的堆栈。
如您在第二个代码示例中所做的那样,可以通过从label_as_result
返回一个函数来实现不会破坏堆栈的不间断版本。
这意味着callCC是一个返回延续的函数callCC
是使用当前延续调用另一个函数的函数。那可以用来返回延续。
延续就像未定的代码,将来需要执行
当然可以,除了在这里我不会说“需要”。您不必调用延续(或者甚至可以多次调用它)。
但是callCC就像需要在Future中执行的预定义代码?
不太清楚您的意思,但这听起来不对。 callCC
是使您可以访问当前延续的功能。
关于javascript - CallCC是goto的改进版本吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59234399/