简短的回答是因为通常不需要在调用点评估块,因为 Rebol 中的块不带参数,所以 大多数情况下 在哪里评估它们并不重要。但是,“大部分”可能需要一些解释......
归结为 Rebol 的两个有趣的特性:静态绑定,以及函数的 do 是如何工作的。
静态绑定和作用域
Rebol 没有范围词绑定,它有静态直接词绑定。有时看起来我们有词法作用域,但我们确实通过在每次构建新的“作用域”代码块时更新静态绑定来伪造它。我们还可以随时手动重新绑定单词。
在这种情况下,这对我们来说意味着,一旦一个块存在,它的绑定和值是静态的 - 它们不受该块的物理位置或正在评估的位置的影响。
然而,这就是它变得棘手的地方,函数上下文很奇怪。虽然绑定到函数上下文的单词绑定是静态的,但分配给这些单词的值集是动态范围的。这是如何在 Rebol 中评估代码的副作用:什么是其他语言中的语言语句是 Rebol 中的函数,例如,对 if 的调用实际上将一个数据块传递给 if 函数 @987654325 @ 然后传递给do。这意味着,当一个函数正在运行时,do 必须从最近一次调用尚未返回的函数的调用帧中查找其单词的值。
这确实意味着,如果您调用一个函数并返回一个代码块,其中单词绑定到其上下文,则在函数返回后评估该块将失败。但是,如果您的函数调用 itself 并且 that 调用返回一个代码块,其中的单词绑定在它上面,那么在函数返回之前评估 that 块将使它在您的函数的当前调用的调用框架中查找这些单词。
无论您是do 还是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]]
代码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它的方式。
然而,这带来了一个有趣的观点:如果你不需要 return/redo 用于块,因为 do 做同样的工作,那么函数不同样适用吗?为什么我们需要return/redo?
函数的 DO 工作原理
基本上,我们有return/redo,因为它使用的代码与我们用于实现函数的do 的代码完全相同。你可能没有意识到,do 的函数确实很不寻常。
在大多数可以调用函数值的编程语言中,您必须将参数作为一个完整的集合传递给函数,类似于 R3 的 apply 函数的工作原理。常规 Rebol 函数调用会导致使用未知提前评估规则对其参数进行一些提前未知数量的附加评估。评估器在运行时计算出这些评估规则,并将评估结果传递给函数。函数本身不处理其参数的评估,甚至不一定知道如何评估这些参数。
但是,当您明确地do 一个函数值时,这意味着将函数值传递给对 另一个 函数的调用,即一个名为 do 的 常规 函数,然后神奇地导致评估附加参数,甚至根本没有传递给 do 函数。
这不是魔法,它是return/redo。函数do 的工作方式是它以常规快捷方式返回值返回对函数的引用,快捷方式返回值中带有一个标志,告诉解释器调用 @987654353 @ 评估返回的函数,就好像它在代码中被调用一样。这基本上就是所谓的蹦床。
这里是 Rebol 的另一个有趣特性:从函数中快捷返回值的能力内置于求值器中,但它实际上并没有使用 return 函数来执行此操作。您从 Rebol 代码中看到的所有函数都是内部内容的包装器,甚至是 return 和 do。我们调用的return 函数只是生成这些快捷方式返回值之一并返回它;其余的由评估者完成。
所以在这种情况下,真正发生的事情是,一直以来我们都有代码可以完成 return/redo 内部所做的事情,但 Carl 决定在我们的 return 函数中添加一个选项来设置该标志,即使内部代码不需要return 这样做,因为内部代码调用内部函数。然后他没有告诉任何人他正在使该选项在外部可用,或者为什么,或者它做了什么(我想你不能提到所有事情;谁有时间?)。根据与 Carl 的对话以及我们一直在修复的一些错误,我怀疑 R2 以不同方式处理函数的 do,这会使 return/redo 不可能。
这确实意味着return/redo 的处理非常彻底地面向函数评估,因为这就是它存在的全部原因。向它添加任何开销都会给函数的do 增加开销,我们使用它一个lot。可能不值得将其扩展到区块,因为我们获得的收益很少,而且我们很少获得任何好处。
不过,对于一个函数的return/redo,我们越想它,它似乎就越有用。在过去的天中,我们想出了各种可以实现的技巧。蹦床很有用。