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

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

问题描述

昨晚我从函数中了解了有关return的/redo选项.它使您可以返回另一个函数,然后在调用站点上调用该函数,并从同一位置重新调用评估程序

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,您将得到:

Thus without return/redo, you'd get:

>> 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函数,而,然后传递到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.

这确实意味着,如果调用函数并返回带有绑定到其上下文的单词的代码块,则该函数返回后,对该块的求值将失败.但是,如果您的函数调用本身并且 that 调用返回绑定了单词的代码块,则在函数返回之前评估那个块将使其在函数的 current 调用的调用框架中查找这些单词.

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的开销. do它的方式.

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 regular 函数)的调用,然后神奇地导致对甚至根本不会传递给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的工作方式是,它以常规的快捷键返回值返回对该函数的引用,并且在快捷键返回值中带有一个标志,该标志告诉解释器称为 评估返回的函数,就像在代码中被调用一样.这基本上就是所谓的蹦床.

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.

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

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