为什么 return/redo 在调用上下文中评估结果函数,但不评估块结果? [英] Why does return/redo evaluate result functions in the calling context, but block results are not evaluated?

查看:12
本文介绍了为什么 return/redo 在调用上下文中评估结果函数,但不评估块结果?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

昨晚我了解了/redo 选项,当您从函数中 return 时.它允许您返回 另一个 函数,然后在调用站点调用该函数并从同一位置重新调用评估器

Last night I learned about the /redo option for when you return from a function. It lets you return another function, which is then invoked at the calling site and reinvokes the evaluator from the same position

>> foo: func [a] [(print a) (return/redo (func [b] [print b + 10]))] 

>> foo "Hello" 10
Hello
20

尽管 foo 是一个只接受一个参数的函数,但它现在 就像一个接受两个参数的函数.否则,类似的事情会要求调用者知道您正在返回一个函数,并且调用者必须手动对其使用 do 评估器.

Even though foo is a function that only takes one argument, it now acts like a function that took two arguments. Something like that would otherwise require the caller to know you were returning a function, and that caller would have to manually use the do evaluator on it.

因此没有return/redo,你会得到:

>> foo: func [a] [(print a) (return (func [b] [print b + 10]))] 

>> foo "Hello" 10
Hello
== 10

foo 使用它的一个参数并按值返回一个函数(没有调用它,因此解释器继续前进).然后表达式计算为 10.如果 return/redo 不存在,您必须编写:

foo consumed its one parameter and returned a function by value (which was not invoked, thus the interpreter moved on). Then the expression evaluated to 10. If return/redo did not exist you'd have had to write:

>> do foo "Hello" 10
Hello
20

这使调用者不必知道(或关心)您是否选择返回要执行的函数.并且很酷,因为您可以执行尾调用优化或为返回功能本身编写包装器之类的事情.这是 return 的一个变体,它打印一条消息但仍退出函数并提供结果:

This keeps the caller from having to know (or care) if you've chosen to return a function to execute. And is cool because you can do things like tail call optimization, or writing a wrapper for the return functionality itself. Here's a variant of return that prints a message but still exits the function and provides the result:

>> myreturn: func [] [(print "Leaving...") (return/redo :return)]

>> foo: func [num] [myreturn num + 10]

>> foo 10
Leaving...
== 20

但函数并不是do 中唯一具有行为的东西.因此,如果这是在调用点消除对 DO 的需要"的一般模式,那么为什么不打印任何内容呢?

But functions aren't the only thing that have behavior in do. So if this is a general pattern for "removing the need for a DO at the callsite", then why doesn't this print anything?

>> test: func [] [return/redo [print "test"]]

>> test 
== [print "test"]

它只是按值返回块,就像正常返回一样.它不应该打印出测试"吗?这就是 do 会......呃,用它来做:

It just returned the block by value, like a normal return would have. Shouldn't it have printed out "test"? That's what do would...uh, do with it:

>> do [print "test"]
test

推荐答案

简短的回答是因为通常不需要在调用点评估块,因为 Rebol 中的块不带参数,所以它主要是 在哪里评估它们并不重要.但是,大部分"可能需要一些解释......

The short answer is because it is generally unnecessary to evaluate a block at the call point, because blocks in Rebol don't take parameters so it mostly doesn't matter where they are evaluated. However, that "mostly" may need some explanation...

归结为 Rebol 的两个有趣的特性:静态绑定,以及函数的 do 是如何工作的.

It comes down to two interesting features of Rebol: static binding, and how do of a function works.

Rebol 没有范围词绑定,它有静态直接词绑定.有时看起来我们有词法作用域,但我们确实通过在每次构建新的作用域"代码块时更新静态绑定来伪造它.我们还可以随时手动重新绑定单词.

Rebol doesn't have scoped word bindings, it has static direct word bindings. Sometimes it seems like we have lexical scope, but we really fake that by updating the static bindings each time we're building a new "scoped" code block. We can also rebind words manually whenever we want.

在这种情况下,这对我们来说意味着,一旦一个块存在,它的绑定和值是静态的 - 它们不受该块的物理位置或正在评估的位置的影响.

What that means for us in this case though, is that once a block exists, its bindings and values are static - they're not affected by where the block is physically located, or where it is being evaluated.

然而,这就是它变得棘手的地方,函数上下文很奇怪.虽然绑定到函数上下文的单词绑定是静态的,但分配给这些单词的值集动态范围的.这是在 Rebol 中如何评估代码的副作用:什么是其他语言中的语言语句是 Rebol 中的函数,因此对 if 的调用,例如,实际上将一个数据块传递给 if 函数,然后 if 将其传递给 do.这意味着当一个函数正在运行时,do 必须从最近一次调用尚未返回的函数的调用帧中查找其单词的值.

However, and this is where it gets tricky, function contexts are weird. While the bindings of words bound to a function context are static, the set of values assigned to those words are dynamically scoped. It's a side effect of how code is evaluated in Rebol: What are language statements in other languages are functions in Rebol, so a call to if, for instance, actually passes a block of data to the if function which if then passes to do. That means that while a function is running, do has to look up the values of its words from the call frame of the most recent call to the function that hasn't returned yet.

这确实意味着,如果您调用一个函数并返回一个代码块,其中单词绑定到其上下文,则在函数返回后评估该块将失败.但是,如果您的函数调用 itself 并且 that 调用返回一个代码块,其中的单词绑定在它上面,那么在函数返回之前评估 that 块将使它在您的函数的当前调用的调用框架中查找这些单词.

This does mean that if you call a function and return a block of code with words bound to its context, evaluating that block will fail after the function returns. However, if your function calls itself and that call returns a block of code with its words bound to it, evaluating that block before your function returns will make it look up those words in the call frame of the current call of your function.

这对于你 doreturn/redo 都是一样的,并且也会影响内部函数.让我演示一下:

This is the same for whether you do or return/redo, and affects inner functions as well. Let me demonstrate:

在函数返回后计算的函数返回代码,引用一个函数字:

Function returning code that is evaluated after the function returns, referencing a function word:

>> 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 和函数中的代码:

Same, but with return/redo and the code in a function:

>> 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 版本,但在对同一函数的外部调用中:

Code do version, but inside an outer call to the same function:

>> do f: function [x] [a: 10 either zero? x [do f 1] [a: 20 [a]]] 0
== 10

相同,但带有 return/redo 和函数中的代码:

Same, but with return/redo and the code in a function:

>> do f: function [x] [a: 10 either zero? x [f 1] [a: 20 return/redo does [a]]] 0
== 10

因此,简而言之,对于块而言,在定义块之外的其他地方执行块通常没有任何优势,如果您愿意,可以更轻松地使用另一个对 do 的调用.自调用递归函数需要返回要在同一函数的外部调用中执行的代码,这是一种极其罕见的代码模式,我从未见过在 Rebol 代码中使用过.

So in short, with blocks there is usually no advantage to doing the block elsewhere than where it is defined, and if you want to it is easier to use another call to do instead. Self-calling recursive functions that need to return code to be executed in outer calls of the same function are an exceedingly rare code pattern that I have never seen used in Rebol code at all.

可以更改 return/redo 以便它也可以处理块,但增加 return/redo 的开销可能不值得添加一个只在极少数情况下有用的功能,并且已经有更好的方法来它.

It could be possible to change return/redo so it would handle blocks as well, but it probably isn't worth the increased overhead to return/redo to add a feature that is only useful in rare circumstances and already has a better way to do it.

然而,这带来了一个有趣的点:如果你不需要 return/redo 块,因为 do 做同样的工作,不同样适用功能?为什么我们需要return/redo?

However, that brings up an interesting point: If you don't need return/redo for blocks because do does the same job, doesn't the same apply to functions? Why do we need return/redo at all?

基本上,我们有 return/redo 因为它使用与我们用于实现函数的 do 完全相同的代码.你可能没有意识到,函数的do真的很不寻常.

Basically, we have return/redo because it uses exactly the same code that we use to implement do of a function. You might not realize it, but do of a function is really unusual.

在大多数可以调用函数值的编程语言中,您必须将参数作为一个完整的集合传递给函数,类似于 R3 的 apply 函数的工作方式.常规 Rebol 函数调用会导致使用未知提前评估规则对其参数进行一些提前未知数量的附加评估.评估器在运行时计算出这些评估规则,并将评估结果传递给函数.函数本身不处理其参数的评估,甚至不一定知道如何评估这些参数.

In most programming languages that can call a function value, you have to pass the parameters to the function as a complete set, sort of how R3's apply function works. Regular Rebol function calling causes some unknown-ahead-of-time number of additional evaluations to happen for its arguments using unknown-ahead-of-time evaluation rules. The evaluator figures out these evaluation rules at runtime and just passes the results of the evaluation to the function. The function itself doesn't handle the evaluation of its parameters, or even necessarily know how those parameters were evaluated.

但是,当您显式执行一个函数值时,这意味着将函数值传递给对另一个函数的调用,一个常规函数命名为 do,然后神奇地导致评估附加参数,这些参数甚至根本没有传递给 do 函数.

However, when you do a function value explicitly, that means passing the function value to a call to another function, a regular function named do, and then that magically causes the evaluation of additional parameters that weren't even passed to the do function at all.

这不是魔法,它是 return/redo.do 函数的工作方式是它以常规快捷方式返回值返回对该函数的引用,快捷方式返回值中带有一个标志,告诉解释器 调用 do 来评估返回的函数,就好像它在代码中被调用一样.这基本上就是所谓的蹦床.

Well it's not magic, it's return/redo. The way do of a function works is that it returns a reference to the function in a regular shortcut-return value, with a flag in the shortcut-return value that tells the interpreter that called do to evaluate the returned function as if it were called right there in the code. This is basically what is called a trampoline.

这里是 Rebol 另一个有趣的特性:从函数中快捷返回值的能力内置在求值器中,但它实际上并没有使用 return 函数来执行此操作.您从 Rebol 代码中看到的所有函数都是内部内容的包装器,甚至是 returndo.我们调用的 return 函数只是生成这些快捷方式返回值之一并返回它;其余的由评估者完成.

Here's where we get to another interesting feature of Rebol: The ability to shortcut-return values from a function is built into the evaluator, but it doesn't actually use the return function to do it. All of the functions you see from Rebol code are wrappers around the internal stuff, even return and do. The return function we call just generates one of those shortcut-return values and returns it; the evaluator does the rest.

所以在这种情况下,真正发生的事情是,一直以来我们都有代码执行 return/redo 在内部执行的操作,但 Carl 决定在我们的 return 函数来设置该标志,即使内部代码不需要 return 这样做,因为内部代码调用内部函数.然后他没有告诉任何人他正在使该选项在外部可用,或者为什么,或者它做了什么(我想你不能提到所有事情;谁有时间?).根据与 Carl 的对话以及我们一直在修复的一些错误,我怀疑 R2 以不同的方式处理函数的 do,其方式会使 return/redo不可能.

So in this case, what really happened is that all along we had code that did what return/redo does internally, but Carl decided to add an option to our return function to set that flag, even though the internal code doesn't need return to do so because the internal code calls the internal function. And then he didn't tell anyone that he was making the option externally available, or why, or what it did (I guess you can't mention everything; who has the time?). I have the suspicion, based on conversations with Carl and some bugs we've been fixing, that R2 handled do of a function differently, in a way that would have made return/redo impossible.

这确实意味着 return/redo 的处理非常彻底地面向函数评估,因为这就是它存在的全部原因.添加任何开销都会给函数的do 增加开销,我们使用一个lot.可能不值得将其扩展到区块,因为我们获得的收益很少,而且我们很少获得任何好处.

That does mean that the handling of return/redo is pretty thoroughly oriented towards function evaluation, since that is its entire reason for existing at all. Adding any overhead to it would add overhead to do of a function, and we use that a lot. Probably not worth extending it to blocks, given how little we'd gain and how rarely we'd get any benefit at all.

对于一个函数的return/redo,我们越想它似乎就越有用.在过去的中,我们想出了各种可以实现的技巧.蹦床很有用.

For return/redo of a function though, it seems to be getting more and more useful the more we think about it. In the last day we've come up with all sorts of tricks that this enables. Trampolines are useful.

这篇关于为什么 return/redo 在调用上下文中评估结果函数,但不评估块结果?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆