- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
有人告诉我,以下表达式旨在计算为 0,但许多 Scheme 实现将其计算为 1:
(let ((cont #f))
(letrec ((x (call-with-current-continuation (lambda (c) (set! cont c) 0)))
(y (call-with-current-continuation (lambda (c) (set! cont c) 0))))
(if cont
(let ((c cont))
(set! cont #f)
(set! x 1)
(set! y 1)
(c 0))
(+ x y))))
call/cc
,但是我能得到这个表达式的详细解释吗?
最佳答案
这是一个有趣的片段。我遇到这个问题是因为我正在寻找有关 letrec
之间确切差异的讨论。和 letrec*
,以及这些在不同版本的 Scheme 报告和不同的 Scheme 实现之间有何不同。在试验这个片段时,我做了一些研究,并将在此处报告结果。
如果你在精神上完成了这个片段的执行,那么两个问题对你来说应该是突出的:
一季度。 x
的初始化子句的顺序是什么?和 y
评价?
Q2。是否首先评估所有初始化子句,并缓存它们的结果,然后是对 x
的所有赋值?和 y
之后执行?还是在评估某些初始化子句之前进行了一些赋值?
对于 letrec
,Scheme 报告称 Q1 的答案是“未指定”。大多数实现实际上会按从左到右的顺序评估子句;但你不应该依赖这种行为。
方案 R6RS 和 R7RS 引入了新的绑定(bind)结构 letrec*
这确实指定了从左到右的评估顺序。它还与 letrec
在其他一些方面有所不同。 ,正如我们将在下面看到的。
返回 letrec
,Scheme 报告至少可以追溯到 R5RS 似乎指定 Q2 的答案是“在进行任何分配之前评估所有初始化子句”。我说“似乎指定”是因为语言并没有像它可能那样明确地说明这一点。事实上,许多 Scheme 实现并不符合这个要求。这就是造成片段的“预期”和“观察到”行为之间差异的原因。
让我们来看看你的片段,记住 Q2。首先,我们为 x
留出两个“位置”(引用单元格)和 y
被绑定(bind)。然后我们评估其中一个初始化子句。假设它是 x
的子句,尽管正如我所说,使用 letrec
它可以是任何一个。我们将此评估的继续保存到 cont
.此评估的结果为 0。现在,根据 Q2 的答案,我们要么立即将该结果分配给 x
或者我们缓存它以便稍后进行分配。接下来我们评估另一个初始化子句。我们将其延续保存到 cont
,覆盖上一个。此评估的结果为 0。现在所有初始化子句都已评估。根据 Q2 的答案,我们此时可能会将缓存结果 0 分配给 x
;或分配给 x
可能已经发生。在任何一种情况下,分配给 y
现在发生。
然后我们开始评估(letrec (...) ...)
的主体表达(第一次)。在 cont
中存储了一个延续,所以我们将它检索到 c
,然后清除 cont
和 set!
各x
和 y
到 1。然后我们用值 0 调用检索到的延续。这回到最后评估的初始化子句---我们假设它是 y
的。然后使用我们提供给延续的参数代替 (call-with-current-continuation (lambda (c) (set! cont c) 0))
,并将分配给 y
.根据 Q2 的答案,将 0 分配给 x
此时可能会或可能不会(再次)发生。
然后我们开始评估(letrec (...) ...)
的主体表达(第二次)。现在 cont
是 #f,所以我们得到 (+ x y)
.这将是 (+ 1 0)
或 (+ 0 0)
,取决于是否将 0 重新分配给 x
当我们调用保存的延续时。
你可以通过用一些 display
装饰你的片段来追踪这种行为。调用,例如像这样:
(let ((cont #f))
(letrec ((x (begin (display (list 'xinit x y cont)) (call-with-current-continuation (lambda (c) (set! cont c) 0))))
(y (begin (display (list 'yinit x y cont)) (call-with-current-continuation (lambda (c) (set! cont c) 0)))))
(display (list 'body x y cont))
(if cont
(let ((c cont))
(set! cont #f)
(set! x 1)
(set! y 1)
(c 'new))
(cons x y))))
(+ x y)
与
(cons x y)
,并用参数
'new
调用延续而不是
0
.
x
init 子句首先和
y
第二条,虽然正如我所说,这种行为是未指定的。
#lang r5rs
和
#lang r6rs
符合 Q2 的规范,因此我们得到了重新分配
0
的“预期”结果调用延续时到另一个变量。 (在试验 r6rs 时,我需要将最终结果包装在
display
中以查看结果。)
(xinit #<undefined> #<undefined> #f)
(yinit #<undefined> #<undefined> #<continuation>)
(body 0 0 #<continuation>)
(body 0 new #f)
(0 . new)
#lang racket
和鸡不符合这一点。相反,在评估每个初始化子句后,它会被分配给相应的变量。因此,当调用 continuation 时,它只会将值重新分配给最终值。
(xinit #<undefined> #<undefined> #f)
(yinit 0 #<undefined> #<continuation>) ; note that x has already been assigned
(body 0 0 #<continuation>)
(body 1 new #f) ; so now x is not re-assigned
(1 . new)
library syntax: (letrec <bindings> <body>)
Syntax: <Bindings> should have the form ((<variable1> <init1>) ...), and <body> should be a sequence of one or more expressions. It is an error for a <variable> to appear more than once in the list of variables being bound.
Semantics: The <variable>s are bound to fresh locations holding undefined values, the <init>s are evaluated in the resulting environment (in some unspecified order), each <variable> is assigned to the result of the corresponding <init>, the <body> is evaluated in the resulting environment, and the value(s) of the last expression in <body> is(are) returned. Each binding of a <variable> has the entire letrec expression as its region, making it possible to define mutually recursive procedures.
(letrec ((even?
(lambda (n)
(if (zero? n)
#t
(odd? (- n 1)))))
(odd?
(lambda (n)
(if (zero? n)
#f
(even? (- n 1))))))
(even? 88))
===> #tOne restriction on letrec is very important: it must be possible to evaluate each <init> without assigning or referring to the value of any <variable>. If this restriction is violated, then it is an error. The restriction is necessary because Scheme passes arguments by value rather than by name. In the most common uses of letrec, all the <init>s are lambda expressions and the restriction is satisfied automatically.
the continuation of each <init> should not be invoked more than once.
letrec*
.这不同于
letrec
有两种方式。首先,它确实指定了从左到右的评估顺序。相应地,上述“限制”可以有所放宽。现在可以引用已经分配了初始值的变量的值:
It must be possible to evaluate each <init> without assigning or referring to the value of the corresponding <variable> or the <variable> of any of the bindings that follow it in <bindings>.
letrec*
,规范现在要求在评估每个初始化子句之后进行赋值。这是来自 R7RS(草案 6)的“语义”的第一段:
Semantics: The <variable>s are bound to fresh locations, each <variable> is assigned in left-to-right order to the result of evaluating the corresponding <init>, the <body> is evaluated in the resulting environment, and the values of the last expression in <body> are returned. Despite the left-to-right evaluation and assignment order, each binding of a <variable> has the entire letrec* expression as its region, making it possible to define mutually recursive procedures.
#lang racket
---以及许多其他实现---实际上似乎实现了他们的
letrec
s 为
letrec*
s。
关于scheme - Letrec 和可重入延续,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13078165/
函数式编程中有一个 CPS 技巧,它采用非尾递归函数并以连续传递样式 (CPS) 重写它,从而轻松地使其成为尾递归。很多问题实际上都涵盖了这一点,例如 https://lorgonblog.wordp
这篇文章是这个 post 的延续 我有 DlUser 类,这个类的每个对象都可能有 DLFaceBook 类,DlFaceBook 的每个对象都可以有映射为 myFriends 的 friend 。
我理解 Reader 或 Maybe 或 State monads 是如何工作的,但在 Continuations monad 上遇到了困难。 像下面这样的例子,吹我的头 type Continuat
协程、延续和生成器之间有什么区别? 最佳答案 我将从生成器开始,因为它们是最简单的情况。正如 @zvolkov 提到的,它们是可以重复调用而不返回的函数/对象,但在调用时将返回(产生)一个值,然后挂起
在下面的代码上调用 await RunAsync(); 时,我希望继续运行 TaskContinuationOptions.OnlyRanToCompletion 继续运行,但是 OnlyOnCanc
我正在使用 jetty-7.4.1.v20110513 和 servlet-api-2.5 我尝试在以下 servlet 中使用连缀。 import java.io.*; import java.
我一直在努力解决 scala 延续的复杂打字问题。我一直在阅读我能找到的所有 Material ,包括关于 continuations 包的引用文档。我想我已经在某种程度上弄清楚了,当你考虑它时它是有
我阅读了很多有关 CosmosDB 分页的文档,并认为 token 应该如下所示: {\"token\":\"xxxxxx\",\"range\":{\"min\":\"xxxxxxxxxx\",\"
我阅读了很多有关 CosmosDB 分页的文档,并认为 token 应该如下所示: {\"token\":\"xxxxxx\",\"range\":{\"min\":\"xxxxxxxxxx\",\"
假设您有服务: interface ISuessService { Task Thing1(); Task Thing2(); } 我有一个扩展方法 ContinueOnUIThrea
我在一段文本上成功应用了 white-space: nowrap。我想知道如果文本被 nowrap 样式截断,是否有可能在文本末尾获得标准的“...”。这是 CSS 可以解决的问题吗?还是我需要 ja
我正在尝试使用 Scala (2.9.0) 延续来构建一个看似阻塞的 API,但这实际上是异步的。假设你想写这样的东西: if(ask("Continue?")) //Prompts Yes/No
我正在使用 Jetty 7 延续来实现一些异步处理。我想做的是开始延续(通过 .suspend()),然后将延续交给其他一些将组成响应的对象,这样效果很好。但是 Jetty 不断将响应(isIniti
协程、延续和生成器之间有什么区别? 最佳答案 我将从生成器开始,因为它们是最简单的情况。正如@zvolkov 提到的,它们是可以重复调用而不返回的函数/对象,但是在调用时将返回(产生)一个值,然后暂停
C++ 11 std::future lacks then 方法将延续附加到 future 。 Boost boost::future provides这个,还有一个example (我无法运行) 我
我在使用 Azure Cosmos DB(通过 .NET SDK)时发现了一些奇怪的东西。 通常,当我使用延续 token 逐页请求查询时,我永远不会获得在创建第一个延续 token 之后创建的文档。
我目前正在实现 System.Web.Http.IActionFilter它调用内部服务来确定当前请求是否可以继续。我遇到的问题是返回 Task基于由 Task 封装的一段逻辑. 一个例子可能会有所帮
我想要一个可序列化的延续,这样我就可以在等待新事件的同时将异步工作流程 pickle 到磁盘。当异步工作流等待 let! 时,它将与唤醒它所需的记录一起保存。而不是内存中的任意 IAsyncResul
我正在努力理解 Continuations 的概念(如 Seaside with Smalltalk 中所使用的)。维基百科的一个片段说: "... refer to first-class cont
我有一个 servlet 过滤器,它充当我的 Web 堆栈的基础。在我的 web.xml 中我有指定我希望过滤器也充当 FORWARD 调度程序。 MyFilter /*
我是一名优秀的程序员,十分优秀!