举例说明:异常、时间旅行搜索、线程等

2020-07-16 23:00:21

使用一级延续,可以将非确定性(或模糊性)选择&34;过程添加到语言中:AMB。AMB过程获取一个值列表,并从中选择一个。这里的问题是,AMB必须选择一个值,以便在计算的将来遇到的所有ASSERT语句都为真。例如,下面的Scheme代码:(#*_)。

((a(amb(List 1 2 3 4 5 6 7)(b(amb(List 1 2 3 4 5 6 7)(c(amb(List 1 2 3 4 5 6 7);我们正在寻找合法权利的维度;使用毕达哥拉斯定理的三角形:(assert(=(*c)(+(*a)(*b);并且,我们希望第二条边较短:(。打印出答案:(display(list a,b,c))(换行符)。

打印输出(4 3 5)。AMB过程选择这些值,这样当稍后遇到断言时,它们就是真的。

有了amb,编写一个SAT解析宏非常简单,因此:计算结果为(#f#f#t)。也就是说,宏(Sat-solve公式正文)绑定公式中的自由变量,以便公式求值为TRUE,然后在这些令人满意的赋值的上下文中求值正文表达式。

具体地说,延续是表示计算中剩余步骤的过程。对于表达式3*(f()+8),考虑在计算表达式f()之后计算中的剩余步骤。例如,在C/Java中,过程CURRENT_CONTINUATION是对f()调用的继续:

void CURRENT_CONTINUATION(Int Result){result+=8;result*=3;(3*(f()+8)的延续)(Result);}。

一些语言(如Scheme和SML/NJ)提供了一种将当前延续捕获为一级值的方法。在Scheme中,带有当前延续的过程调用(通常缩写为call/cc)接受一个过程,并传递当前计算的延续。

在表达式主体的执行过程中,变量current-Continution被绑定到当前Continue,如果被调用,current-Continution会立即从call/cc调用返回,call/cc返回传递给current-Continution的任何值。

(DISPLAY(call/cc(lambda(Cc)(display";I get here.\n";)(cc";此字符串已传递给延续.\n";)(display";但不在此处。\n";)。

一流的延续之所以如此强大,是因为即使在对call/cc的调用完成之后,仍然可以调用该延续。例如,以下代码导致打印将永远调用(启动)的无限循环:

(让((start#f))(if(Not Start)(call/cc(lambda(Cc)(set!start cc)(DISPLAY";将调用(START)\n";)(START)。

就其本身而言,call/cc可以对Continuations做任何您可能需要做的事情,但它不是最方便的API。当使用Continuations进行编程时,我建议使用a helper过程current-Continution:

然后,您可以使用条件模式来检测连续是刚刚创建的,还是从以后的某个点调用了延续:

用户定义的谓词未来值?应该能够识别传递给Continue的值。(有些Scheme实现支持Continue?,应该使用哪个而不是Procedure?(如果有的话。)。

现在,我们可以在简介中揭示如何编写预告式示例。在幕后,过程amb和assert通过失败堆栈相互通信。";失败堆栈的顶部包含只要断言失败就要调用的下一个延续。当断言失败时,它调用过程Fail,该过程弹出失败堆栈并调用结果。amb过程在失败堆栈之上推入一个新的Continue,并选择。

异常很容易用大陆实现,当抛出异常时,控制需要返回到第一个封闭的try窗体。

要使用Continuations创建此行为,tryforms在评估其主体之前捕获当前Continue。在Scheme中,try的实现还将使用Dynamic-Wind来维护异常处理程序堆栈。

在评估(Do-Tunk Do-thunk Do-thunk)时,运行时将确保每当执行进入Do-Tunk内的计算的动态范围时都会调用BEFORE-THUNK,而每当执行离开计算的动态范围时就会调用After-Tunk。

try块可以通过使用之前的thunk来推送异常处理程序,使用之后的thunk来弹出异常处理程序来利用此行为。

生成器是迭代过于复杂的数据结构(如树和图形)问题的一种很好的解决方案。想象一下,您想要编写如下所示的for循环:一些语言(如Java)允许自定义迭代器,因此您可以编写如下内容:但是,如何编写迭代器t?您可以遍历树并将其转换为数组或列表,但这效率很低--您分配的资源比需要的多,实际上您最终只能迭代两次。有效的解决方案(在Java这样的语言中)是编写一个迭代器。而且它不是#39;I don‘我感觉不自然。(试着写出来!)。

简单的树遍历更容易编写,如果语言支持一级延续,那么您可以使用树遍历方法来迭代,而不需要中间数据结构的开销。

生成器是一个接受Year过程作为参数的过程,期望它将迭代某些数据结构,并在数据结构中的每个元素上调用Year。通过延续,我们可以检测生成器,使其适合于无开销FOR样式的迭代。

关键思想是在两个Continuation之间切换--一个在TheLoop中,另一个在生成器中,当循环需要另一个值时,它会切换到生成器内部的Continuation(传递给它一个循环的Continution),一旦生成器有了一个值,它就会传回一个值(加上生成器的一个新的Continue)。

延续使得实现协作性多线程变得简单(并且使用宏,模拟抢占式多线程并不困难)。

在协作式多线程中,线程必须手动放弃控制;它不会被抢占地切换出去。