(让*(阴((λ(cc)(显示“@”)cc)(杨)((λ(cc)(显示“*”)cc)(阴阳)

这个呼叫/抄送阴阳拼图已写入方案。通常在讨论“call-with-current-continuation”结构时提出,通常缩写为呼叫/抄送以防止重复性劳损-在编程中,因为它严重依赖于函数。它最初是由大卫·马多雷,的创建者Unlambda编程语言,试图编写打印连续整数的Unlambda程序。

运行时,阴阳谜题的输出为“@*@**@***@***@@***@*****@******@*******…”,即连续较大的数字星号s由分隔现场s.许多人都曾尝试过,但都未能找出确切的原因;这就是延续的本质。

为了理解阴阳之谜到底有多令人困惑,有必要了解一些关于延续的背景知识。如果你对延续一无所知,这一部分是必不可少的。如果你完全摸索并认为你很好,无论如何都要读它.

继续是,为了掩盖几个要点,对程序执行的未来进行客观化;基本上,他们不只是代表,实际上体现-程序在任何给定时刻都要做什么。为了将他们难以置信的能力传递给程序员,他们需要一种带有一流的功能:该语言中的函数可以作为变量赋值,并像任何其他数据类型一样传递给其他函数。这些语言的一些例子是路易斯安那州,方案(和其他LISP公司s) ,哈斯克尔(和其他功能语言),红宝石(在一定程度上),蟒蛇,JavaScript脚本、和乌兰姆达(请注意,并非所有这些语言都支持(甚至可以支持)call/cc。)

举个例子,假设,不失一般性我们的程序(用bastard-tacular编写伪码)计算斜边通过勾股定理.

定义函数pytag(x,y){返回sqrt(x*x+y*y);}

为了便于讨论,让我们再详细一点。

定义函数pytag(x,y){x2:=x*x;y2:=y*y;z2:=x2+y2;z:=sqrt(z2);返回z;}

在函数中,您可以清楚地选择程序为获得结果所采取的步骤。将x平方,y平方,相加,求根。例如,y2赋值处的继续是将该值加到x2上,然后求出结果的平方根。x2计算的延续将为y的平方,并且然后继续y2继续考虑一下。

现在,如前所述,在call/cc语言中,函数是一级值。call/cc接受一个参数,一个函数,它本身应该只接受一个变量。然后,它继续调用/cc调用—当前延续-并将其作为唯一参数传递给传递的函数。例如,假设continuation对象可以表示为<cont>,则callcc(f)变为f(<cont>)。当您稍后传递<cont>a值时,它“返回”到callcc(f)语句并继续使用该值。

你现在可能会感到困惑。别担心。最谨慎的思考方式是这样的。

callcc(f)应该有一个返回值,对吗?因此,快照调用/cc的继续,即它后面的程序的其余部分,并将其转换为(一级)函数。当你给它一个参数时,这个函数将执行程序的其余部分。(如果该参数是任何其他函数,则通常由callcc(f)的返回值提供。)现在,将这个continuation函数传递给f,并让它执行。如果f从不使用<cont>,那么只需在需要callcc(f)返回值的地方使用f(<cont>)的返回值。但是,如果确实调用了<cont>,那么取传递给它的值,并从您用callcc(f)中断的位置继续!

作为视觉参考,请看下面的示例。(从现在开始,我们将使用LISP符号,因为对于调用/cc和我们正在使用的一级/高阶函数概念来说,这要自然得多。)

(-(调用/cc(λ(k)(+(k 42)1729))16)=>(-(λ(k)(+(k 42)1729))<续>)16)=>(-(+(<cont>42)1729)16)**FWOOSH魔法电视**(-(调用/cc(λ(k)(+(k 42)1729))16)=> (- 42 16);; call/cc“返回”收到的42=>26

你现在可能有几个后续问题。

  • What happens to the continuation of the(k 42)(添加到1729)?它不见了。它已被删除,支持继续“继续”。这很大程度上是因为具体化方案中的延续基于对调用堆栈因此,当continuation最终被调用时,嵌入其中的调用堆栈的副本将替换原来的调用堆栈,并且旧的执行路径在垃圾收集后将停止存在。
  • 如果lambda中没有使用continuation(如果(+(k 42)1729)阅读(+ 42 1729))?然后将1729加上42,得出1771,再减去16,得出1755。也就是说,continuation只会保持未使用状态,而对(call/cc(some-lambda))的调用会被重写为((some-lambda,continuition-that-is-never-used),从它停止的地方继续执行,而不必大惊小怪。
  • 如果我们在call/cc'ed函数中或程序中稍后使用另一个call/cc会怎么样?对。

还有最后一个重要注意事项。有些方面存在于延续之外。其中最有用的是输入/输出流,例如标准打印功能。这意味着在你可能会想到的任何形式的连续恶作剧中打印的任何内容保持打印状态这是理解阴阳之谜以及其他许多涉及延续的节目的关键方面。另一个永久性构造,这个更显式的永久性构造是Scheme的设置!函数:它基本上就像一个变量赋值,在整个程序中持续存在,而不管您设置了什么样的延续迷宫。(事实上,如果您引入基于这些变量中的值的条件,它可能会影响延续迷宫的控制路径。)就上述调用堆栈解释而言,人们只会认为变量存在于解释器中的某些内部查找中,而不是程序空间中。

互联网上有很多资源专门帮助普通的Joe程序员理解call/cc马多尔自己的互联网圣地、和Scheme Wiki的分类教程。我在这里对call/cc的处理绝不完整,因此如果您发现这种编码,请随时继续受虐狂令人感到安慰。

意义

我们现在几乎可以解决这个难题,但我们必须处理程序的每个部分的实际含义。为了便于理解,这些定义中的一些与标准Scheme用法略有不同,所以我会在必要时省略、掩饰或不正确地解释;不要期望从这里收集到的任何知识可以直接应用于Scheme编程,而不必对照r5年第一。

显示是一个明显的打印功能。呼叫/抄送是我们在最后一节讨论的内容。

lambda构造是一种编写匿名函数的方法,即不需要名称或完整定义就能工作的函数(λ(变量1变量2…)主体)。它接受与变量列表(var1等)中的变量一样多的参数,将其绑定(意味着将调用时传递的值附加到定义时指定的名称),然后使用绑定的适当变量名称执行正文中的所有函数(可以有多个函数)。例如,((lambda(x y)(sqrt(+(*x x)(*y)))3 4)将x绑定到3,将y绑定到4,然后计算为5。如果有多个正文语句,则依次执行每个语句,但只有最后一个是返回值。例如,((lambda(x y)(display y)(显示x)(sqrt(+(*x x)(*y)))34)将打印4和3,然后返回毕达哥拉斯对角线5。

在我们的特定情况下,(lambda(c)c)是标识函数,它返回给定的任何内容,而(lambda(cc)(display“@”)cc)几乎相同,只是它在调用时打印一个@。类似地,(lambda(cc)(显示“*”)cc)在返回其传递的值之前打印*。

莱特是一只有趣的野兽。它来自let函数家族,该家族创建变量绑定。在表单中使用(出租*(绑定)主体),其中绑定的形式为((var1 init1)(var2 init2)…)。使用let,所有initN值都会通过调用逻辑-LISP-performs-is来计算/评估,然后绑定到各个varN变量,这样就可以在body的内容中通过名称引用它们。明确地说,首先计算所有init1到initN,然后通过varN绑定到var1。然而,使用let*,绑定是按顺序进行的:首先计算init1并绑定到varl,然后init2绑定到var2,依此类推。从var1到varN的所有变量都对可见,因此在计算initNplus1期间,可以由in-varNplusl使用,也可以在其中引用。(还存在一个letrec,其中绑定发生在计算之前,然后填充值,从而允许递归定义,但这对我们现在来说并不重要。)

有了这些知识,我们可以剖析阴阳之谜。

阴阳

首先要注意的是,拼图的一般形式是一个单独的let*结构,先是阴绑定,再是阳绑定,然后是阴对阳的应用。这向我们表明,阴总是在一个新鲜的环境中定义的,而阳只存在于阴之后,即在阴的环境中延续.

现在,我们注意到每个绑定的结构。首先检查yin,我们看到(call/cc(lambda(c)c))在那一点生成了一个continuation,并将其求值为(lambdac)<cont>),这通常会简化为<cont>。然后,这个延续被传递给(lambda(cc)(display“@”)cc),后者打印一个@,然后返回其参数。总之,生成了一个延续,打印了一个@,然后将阴绑定到该延续。

请注意,((lambda(cc)(显示“@”)cc)和(call/cc(lambdacc))不是一回事。前者的延续是即将传递给打印lambda,而后者的延续为的外部印刷的lambda,就在装订之前。这一区别很快就会变得重要。

现在看看阳,我们看到它是一样的,除了两件事。首先,它打印*而不是@其次,殷已经被绑定到一个延续。当阴被束缚时,就没有阳;然而,当阳被束缚时,已经存在一个阴。这一点值得注意因为延续.继续就是原因。

因此,阴阳中的每一个都按顺序得到延续,然后它们被一个接一个地应用。我们目前在输出流中有“@*”,因为当在阴阳绑定中计算continuation时,它们会通过特定的打印机。这就是奇迹发生的地方。

要稍微消除阴阳都有延续的含义的歧义,请考虑生成这两个延续的时间点的未来。to-be-yin continuation表示通过@-printer,然后绑定到yin,然后评估并绑定yang到continuant,最后评估(阴阳)。然而,to-be-yang延续已经有了阴界限;它所要做的就是通过*-打印机,绑定到yang,然后计算(阴阳),再次绑定到yin。这与阴的延续相反,阴和阳都没有被束缚。

让我们将当前存在的阴阳延续分别表示为C0和C1。(阴阳)则为(C0 C1)。这将调用C0延续,C1作为返回值。我们回到了C0的产生,它位于阴结合线上。给我们C0的调用/cc现在返回值C1,该值通过@打印机,然后绑定到yin。现在输出为“@*@”。

现在我们必须再次约束杨。我们再次调用/cc的恒等函数,但现在注意到,阳的未来将出现在(阴阳)中,其中阴是C1,而不是像以前那样的C0。因此,杨产生了一个新的延续,叫做它指挥与控制-他们的未来是通过*-打印机,绑定到yang,然后评估(阴阳)=>(C1<new-yang-cont>)。

所以这个C2经历了传递标识函数、传递*-打印机(现在输出为“@*@*”)和绑定到yang的动作。阳现在是C2,所以(阴阳)=>(C1 C2)。

C1“返回”C2。由于C1被生成为yang的延续,在C0被绑定到yin之后,yang调用/cc现在返回C2。然后打印一个星号(输出“@*@**”),并与阴为C0的阳绑定。(阴阳)=>(C0 C2)。

C0“返回”C2。C0是在yin绑定中生成的,因此yin调用/cc返回C2。打印@(输出“@*@@**@”),C2绑定到阴。这将为yang呼叫/cc启动一个新的延续,C3类,它的未来是打印星号,绑定到yang,并作为in=>C2的参数传递。C3打印星号(输出“@*@@**@*”),并与阴为C2的阳结合。(阴阳)=>(C2 C3)。

C2由阴为C1的阳生成。打印星号(“@*@@**@**”),yang=>C3,(阴阳)=>(C1 C3)。

C1由阴为C0的阳生成。打印星号(“@*@**@***”),阳=>C3,(阴阳)=>(C0 C3)。

C0是为yin生成的,所以现在打印一个@(“@**@**@***@”),yin=>C3。现在你可以看到图案的形成。我们可以猜测,按照之前所有续集的做法,杨将制作一个补体第四成份,它将沿着C3、C2、C1的行传递到C0,这将把它绑定到一个阴并创建一个新的延续C5。然后C5将继续下线,依此类推。

下面是一个简短的摘要,详细介绍了上面的解释,正如直接评估代码所看到的那样。为了保持简短,(lambda(c)c)被重写为i,打印的lambda分别为p@和p*。

(让*((阴(p@(呼叫/cci)))(阳(p*(呼叫/cc i))))=>(let*((yin(p@(i C0)))(yang(p*(call/cci))))=>(let*((yin(p@C0))(yang(p*(call/cci)))(阴阳))=>(let*((阴C0)(阳(p*(呼叫/抄送i)))(阴阳)); @=>(让*((阴C0)(阳(p*(i C1)))(阴阳)); @=>(让*((阴C0)(阳(p*C1)))(阴阳)); @=>(让*((阴C0)(阳C1))(阴阳)); @*=>(C0 C1); @*;; C0返回C1; @*(让*((阴(p@(呼叫/cci)))(阳(p*(呼叫/cc i))))@*=>(let*((yin(p@C1))(yang(p*(call/cci)))(阴阳)); @*=>(让*((阴C1)(阳(p*(呼叫/cci)))(阴阳))@*@=>(让*((阴C1)(阳(p*(i C2)))(阴阳)); @*@=>(让*((阴C1)(阳(p*C2)))(阴阳)); @*@=>(让*((阴C1)(阳C2))(阴阳)); @*@*=>(C1 C2); @*@*;;C1返回C2; @*@*(让*((阴C0)(阳(p*(呼叫/cci))))(阴阳)); @*@*=>(让*((阴C0)(阳(p*C2))(阴阳)); @*@*=>(让*((阴C0)(阳C2))(阴阳)); @*@**=>(C0 C2); @*@**;; C0返回C2; @*@**(让*((阴(p@(呼叫/cci)))(阳(p*(呼叫/cc i)))); @*@**=>(let*((yin(p@C2))(yang(p*(call/cci)))(阴阳)); @*@**=>(让*((阴C2)(阳(p*(呼叫/cci)))(阴阳)); @*@**@=>(让*((阴C2)(阳(p*(i C3)))(阴阳)); @*@**@=>(让*((阴C2)(阳p*C3))(阴阳)); @*@**@=>(让*((阴C2)(阳C3))(阴阳))@*@**@*=>(C2 C3); @*@**@*;; C2返回C3; @*@**@*(让*((阴C1)(阳(p*(呼叫/cci))))(阴阳)); @*@**@*=>(让*((阴C1)(阳p*C3))(阴阳)); @*@**@*=>(让*((阴C1)(阳C3))(阴阳)); @*@**@**=>(C1 C3)@*@**@**;; C1返回C3; @*@**@**;; 等。。。

从C[N]延续到C[N-1]的每一次“传递”都会打印一个星号。当将最近的延续传递给C0时,会打印一个@。然后形成一个新的延续,并打印另一个星号,然后星号打印链重新开始,这一次增加一个成员。

这就是延续的本质。

量化宽松政策.



来源,无特定顺序:
  1. http://stackoverflow.com/questions/2694679/how-does-the-yin-yang-puzzle-work
  2. http://stackoverflow.com/questions/2827620/call-cc-in-lua-possible/2828499#2828499
  3. https://groups.google.com/forum/?fromgroups=#!主题/comp.lang.scheme/Fysq_Wplxsw
  4. https://groups.google.com/forum/?fromgroups=#!主题/comp.lang.scheme/pUedvrKYY5w
  5. http://people.csail.mit.edu/jaffer/r5rs_6.html
  6. http://community.schemewiki.org/?调用-含电流
  7. http://www.madore.org/~david/computers/callcc.html
  8. http://www.madore.org/~david/programs/unlabda/
此外,Madore是E2用户!令人惊叹的!

登录登记在这里写点什么或联系作者。