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