为什么 return/redo 在调用上下文中评估结果函数,但不评估块结果?

2023-11-22

昨晚我了解了 /redo 选项,当你return来自一个函数。它可以让你回来another函数,然后在调用站点调用该函数并从同一位置重新调用评估器

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

>> foo "Hello" 10
Hello
20

虽然foo是一个只接受一个参数的函数,现在它其行为就像一个带有两个参数的函数。否则,类似的事情会要求调用者知道您正在返回一个函数,并且该调用者必须手动使用do评估者对其进行评估。

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

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

>> foo "Hello" 10
Hello
== 10

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

>> do foo "Hello" 10
Hello
20

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

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

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

>> foo 10
Leaving...
== 20

但函数并不是唯一具有行为的东西do。所以如果这是一个通用模式“消除了呼叫站点对 DO 的需要”,那为什么不打印任何东西呢?

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

>> test 
== [print "test"]

它只是按值返回块,就像正常的返回一样。它不应该打印出“test”吗?就是这样do会...呃,用它做:

>> do [print "test"]
test

简短的答案是因为通常不需要在调用点评估块,因为 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.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 return/redo 在调用上下文中评估结果函数,但不评估块结果? 的相关文章

  • 我需要帮助了解 Python 的 return 语句及其在该递归语句中的作用

    不 这不是家庭作业 而是我们的测试学习指南上的内容 我需要了解 return 语句所扮演的角色以及递归所扮演的角色 我不明白为什么该函数在 x 1 后不会中断 def thisFunc x print x if x gt 1 result
  • C26444 避免使用自定义构造和销毁未命名对象(es.84)

    谁能帮我解决问题吗 以前我只有一种类方法 它是无效的显示 向量与列表 字符串和过滤器 get display函数在里面 然后我决定将这些函数分成矢量 get 和 void display 但是当我返回一个新的修改向量时向量获取 出现错误 C
  • Javascript 中嵌套函数的返回值[重复]

    这个问题在这里已经有答案了 我有一个设置如下的函数 function mainFunction function subFunction var str foo return str var test mainFunction alert
  • 这是尾调用吗? (Javascript)

    假设您有一个递归函数 例如 Blah prototype add function n this total n this children forEach function child child add n Is the child a
  • 函数返回与不返回?

    返回还是不返回 是函数的问题 或者说 这真的很重要吗 故事就这样开始了 我曾经编写如下代码 Type3 myFunc Type1 input1 Type2 input2 但最近我的项目学院告诉我 我应该尽可能避免编写这样的函数 并建议采用以
  • 为什么我的代码没有返回任何内容

    目前对编程还很陌生 正在尝试学习Python 我有这段代码 但我不明白为什么我没有得到返回值 balance 3200 annualInterestRate 0 2 monthlyInterestRate annualInterestRat
  • 为什么程序会给出“非法类型开始”错误?

    这是相关的代码片段 public static Rand searchCount int x int a int b int c int d int f int g int h int i int j Rand countA new Ran
  • 如何让键盘显示返回键?

    我想我已经尝试了所有组合 但我无法让字母键盘显示返回键 它始终是一个 完成 按钮 没有什么用处 在 Nexus 7 4 1 上 情况更糟 并显示一个愚蠢的笑脸按钮和 完成 按钮 这对我的应用程序没有任何意义 只要有返回按钮 就可以有 完成
  • 函数不会在所有代码路径上返回值。使用结果时,运行时可能会发生空引用异常

    我收到此错误 函数 getkey 不会在所有代码路径上返回值 当结果为空引用异常时 可能会在运行时发生 用过的 到以下代码 Public Function getkey ByVal id As String Dim cmd As SqlCo
  • lambda 始终返回“1”

    有这样的代码 include
  • 如何确定函数是否不返回任何内容?

    有没有办法在 PHP 中使用反射或其他方法来做到这一点 function a return null function b a a null b b null 如果您没有显式返回某些内容 则函数将返回null默认情况下 这就是 PHP 中函
  • C++:如何从函数返回shared_ptr

    当尝试从函数返回shared ptr时 我得到 对局部变量 recipe 的引用返回 Werror return local addr 我哪里做错了 shared ptr
  • 从 WCF 服务返回接口

    我有一些 NET 远程处理代码 其中在某些服务器端类中实现的工厂方法返回具体对象的接口 也在同一台服务器上执行 NET 远程处理会自动创建代理 并允许我将接口传递给客户端 然后客户端可以直接调用它们 接口示例 public interfac
  • 这里的退货是如何进行的?

    新问题 但是为什么表达式 from n number from firstFactorPtr Factor1 有人可以向我解释一下这是如何工作的吗 返回 因子 1 这将返回到 main 其中 factor 1 为什么 isPrime 返回t
  • 返回吃异常

    我至少发现了以下行为weird def errors try ErrorErrorError finally return 10 print errors prints 10 It should raise NameError name E
  • 获取模板lambda参数的返回值,如何简化代码?

    这是我的技巧 template
  • 单个返回语句与多个返回语句? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 有没有办法从 javascript 中的子函数调用父函数的 Return ?

    我遇到了一个非常特殊的案例 我想要return一些数据 通过ajax下载的数据 到目前为止 异步和同步模式无法及时获取数据return 我可以打电话吗return从子函数到父函数或者超时可以解决问题吗 我想不出另一种方法来做到这一点 但数据
  • 类返回语句不打印任何输出

    我正在学习课程 但遇到了问题return语句 它是语句吗 我希望如此 程序什么也没有打印出来 它只是结束而不做任何事情 class className def createName self name self name name def
  • 从 ES6 箭头函数返回对象文字

    如果有人解释为什么在 UpdatedPosts 中我们应该返回一个对象 而在一个对象内我们应该再次返回它 我将不胜感激 为什么我们可以只做这种和平的代码 gt const UpdatedPosts posts map 数据 gt 数据 作者

随机推荐