- 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/
两者 letrec和 letrec*在 R6RS 中,但有 only letrec在 Racket 中,没有 letrec* .这些之间有什么区别? 最佳答案 总之 Racket letrec和 R6
Kent Dybvig 在 The Scheme Programming Language for letrec 和 letrec* 中给出的两个示例是: (letrec ([sum (lambda
有人告诉我,以下表达式旨在计算为 0,但许多 Scheme 实现将其计算为 1: (let ((cont #f)) (letrec ((x (call-with-current-continuat
我正在尝试使用match-lambda模式匹配对letrec的调用。在我看来,这种模式: (match-lambda (`(letrec ((, ,) . (, ,)) , . ,) `()
在阅读“The Seasoned Schemer”时,我开始了解letrec。我了解它的作用(可以用 Y-Combinator 复制),但本书使用它来代替对已经defined 函数的重复操作,该函数对
怎么可以letrec无需使用 set! 即可实现? 在我看来 set!是一种命令式编程结构,如果使用它,就会失去函数式编程的好处。 最佳答案 我知道通常我们要求复制内容,但您的问题没有简短的答案。 h
我正在用 Scheme (DrRacket Pretty Big) 编写一个愚蠢的 letrec: (letrec ((is-creative? (lambda (writing)
R5RS 为库形式的语法提供了建议的宏定义: http://schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-10.html#%_sec_7.3
Core Erlang 的 letrec 有什么用? Richard Carlsson 在 "Introduction to Core Erlang" 中写道: Furthermore letrec
这个问题已经有答案了: Confused by the difference between let and let* in Scheme (2 个回答) 已关闭10 年前。 let、let* 和 l
我只是在玩一个 NFA 来识别字符串。我有一个宏,它创建一个函数,该函数消耗输入并将其余部分传递给其他一些函数。因为我的 NFA 图中可能存在循环,所以我使用 letrec 将整个图组合在一起。这是一
我有几本关于 Scheme 的书,其中一些提到了名为 let 和 letrec 的书,但实际上没有一本会给出令人信服的例子(我的意思是,我何时以及为什么会使用一个而不是另一个)。 有没有letrec/
我无法理解维基百科上给出的 HM 系统的 letrec 定义,这里:https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system#R
我是计划的初学者。我曾经在使用 letrec 和使用 lambda 绑定(bind)时执行过类似的程序。 (define (drop l n) (letrec ((iter (lambda(ls
考虑在方法主体中定义为 lambda 表达式并分配给变量的阶乘函数: Func factfail = n => { if (n == 0) return 1; else
我不明白两者之间有什么区别(对于人为的例子感到抱歉): (define average (lambda (elems) (define length (lambda (xs)
我是一名优秀的程序员,十分优秀!