简短的答案是因为通常不需要在调用点评估块,因为 Rebol 中的块不带参数,因此它mostly在哪里评估并不重要。然而,这个“大部分”可能需要一些解释......
这归结为 Rebol 的两个有趣的特性:静态绑定,以及如何do
一个函数的工作原理。
静态绑定和范围
Rebol 没有作用域字绑定,它有静态直接字绑定。有时,我们似乎拥有词法作用域,但实际上我们通过每次构建新的“作用域”代码块时更新静态绑定来伪造这一点。我们还可以随时手动重新绑定单词。
不过,在这种情况下,这对我们来说意味着,一旦块存在,它的绑定和值就是静态的 - 它们不受块的物理位置或评估位置的影响。
然而,这就是棘手的地方,函数上下文很奇怪。虽然绑定到函数上下文的单词的绑定是静态的,一组值分配给这些词的是动态地范围。这是 Rebol 中代码评估方式的副作用:其他语言中的语言语句是什么,Rebol 中的函数,因此调用if
,例如,实际上将一个数据块传递给if
函数其中if
然后传递到do
。这意味着当一个函数运行时,do
必须从最近一次调用尚未返回的函数的调用框架中查找其单词的值。
这确实意味着,如果您调用函数并返回一个代码块,其中单词绑定到其上下文,则在函数返回后评估该块将失败。但是,如果您的函数调用itself and that调用返回一个代码块,其单词绑定到它,评估that函数返回之前的块将使其在调用框架中查找这些单词current调用你的函数。
这对于您来说都是一样的do
or return/redo
,并影响内部功能。让我演示一下:
函数返回在函数返回后计算的代码,引用函数字:
>> a: 10 do do has [a] [a: 20 [a]]
** Script error: a word is not bound to a context
** Where: do
** Near: do do has [a] [a: 20 [a]]
相同,但与return/redo
以及函数中的代码:
>> a: 10 do has [a] [a: 20 return/redo does [a]]
** Script error: a word is not bound to a context
** Where: function!
** Near: [a: 20 return/redo does [a]]
Code do
版本,但在对同一函数的外部调用内:
>> do f: function [x] [a: 10 either zero? x [do f 1] [a: 20 [a]]] 0
== 10
相同,但与return/redo
以及函数中的代码:
>> do f: function [x] [a: 10 either zero? x [f 1] [a: 20 return/redo does [a]]] 0
== 10
简而言之,对于块,除了定义它的地方之外,在其他地方执行块通常没有任何优势,并且如果您愿意,使用另一个调用会更容易do
反而。自调用递归函数需要返回要在同一函数的外部调用中执行的代码,这是一种极其罕见的代码模式,我从未在 Rebol 代码中看到过这种模式。
或许可以改变return/redo
所以它也可以处理块,但可能不值得增加开销return/redo
添加一个仅在极少数情况下有用并且已经有更好的方法的功能do
it.
然而,这提出了一个有趣的观点:如果你不需要return/redo
对于块,因为do
做同样的工作,功能不也一样吗?为什么我们需要return/redo
at all?
函数 DO 的工作原理
基本上,我们有return/redo
因为它使用与我们用来实现完全相同的代码do
的一个函数。你可能没有意识到,但是do
一个函数确实不寻常。
在大多数可以调用函数值的编程语言中,您必须将参数作为一个完整的集合传递给函数,就像 R3 的方式一样apply
功能有效。常规 Rebol 函数调用会使用未知的提前评估规则对其参数进行一些未知的提前数量的附加评估。评估器在运行时计算出这些评估规则,并将评估结果传递给函数。函数本身不处理其参数的评估,甚至不一定知道how对这些参数进行了评估。
然而,当你do
显式函数值,这意味着将函数值传递给调用another函数,一个regular函数名为do
,然后神奇地导致附加参数的评估甚至没有传递给do
完全发挥作用.
嗯,这不是魔法,而是return/redo
。道路do
函数的工作原理是,它以常规快捷方式返回值返回对函数的引用,并在快捷方式返回值中带有一个标志,告诉解释器:called do
评估返回的函数,就像在代码中调用它一样。这基本上就是所谓的蹦床。
这是 Rebol 的另一个有趣的功能:从函数快捷返回值的能力内置于求值器中,但它实际上并没有使用return
函数来做到这一点。您从 Rebol 代码中看到的所有函数都是内部内容的包装器,甚至return
and do
. The return
我们调用的函数只是生成这些快捷方式返回值之一并返回它;评估员会完成剩下的工作。
所以在这种情况下,真正发生的事情是我们一直都有代码做这些事情return/redo
内部做,但卡尔决定添加一个选项到我们的return
函数来设置该标志,即使内部代码不需要return
这样做是因为内部代码调用内部函数。然后他没有告诉任何人他正在向外部提供该选项,或者为什么,或者它做了什么(我猜你不能提及所有内容;谁有时间?)。根据与 Carl 的对话以及我们一直在修复的一些错误,我怀疑 R2 处理了do
函数的不同方式,以某种方式使return/redo
不可能的。
这确实意味着处理return/redo
非常彻底地面向功能评估,因为这是它存在的全部原因。添加任何开销都会增加开销do
一个函数,我们使用它lot。可能不值得将其扩展到区块,因为我们获得的收益是如此之少,而且我们获得任何好处的机会又是多么少。
For return/redo
不过,我们对函数的思考越多,它似乎就变得越来越有用。在最后day我们想出了各种各样的技巧来实现这一点。蹦床是useful.