如何从概念上理解收益以及如何/何时使用

如果您正在像我一样学习一种新的编程语言,则可能已经接触过yield运算符。 第一次介绍时,我未能掌握yield的功能以及实用性。 在本文中,我将研究几个yield及其工作方式,并使用几个示例进行深入了解。
由于yield存在于多种编程语言中,因此在最终学习其他语言时,了解其基本功能将对您有帮助。 本文的目的是证明yield操作符是完善您的编程库的强大工具。
收益基础
“许多主流的编程语言,例如Ruby,Python,C#和JavaScript,都具有称为yield的运算符的变体。”
— RP James和A. Sabry。 “收益:主流定界延续”
在每种语言中, yield都有细微的差异。 认识到这一点很重要,因为通过每种语言调用yield不会收到相同的结果。 例如,Ruby中的yield返回结果,而C#中的yield不返回结果(RP James和A. Sabry)。
但是, yield的主要功能在所有语言中都保持不变,这就是“(提供)一种暂时中止计算并具有稍后恢复能力的方法”(RP James和A. Sabry)。 除了学术上的白话, yield使我们可以在执行一种方法时暂停,调用要执行的特定代码块, 然后返回以完成原始方法中的代码。
关于块的快速说明…
在演示yield示例之前,我首先要定义一个代码块。 代码块或代码块只是对一个或多个代码语句进行分组的一种方式。 例如,在Ruby中,您会在do / end关键字或花括号之间找到代码块。
示例1(Ruby中的所有示例):
1个数字= [1、2、3、4]
2
3个数字。每个| num | #<---程序段开始
4 把“#{num}平方等于:#{num ** 2}”
5 end #<---块的结尾(块以粗体突出显示)
示例1的输出:
1平方等于:1
2平方等于:4
3平方等于:9
4平方等于:16
=> [1、2、3、4]
范例2:
1 def plus_five(数组)
2 array.map {| num | num + 5} # { }
3端
4
5 plus_five(数字)
示例2的输出:
=> [6、7、8、9]
为了使本文专注于yield ,我不会详细研究这些示例中发生的情况。 请只注意上面每种情况下的方块。 在第一个示例中,代码块是do / end语句之间的所有内容。
第二个示例有两个代码块。 该方法本身是一个代码块,花括号之间的所有内容都是第二个代码块。 该方法充当一个块:当我们在第5行调用它时,其中的所有代码都在传递给我们的方法的参数上执行。 同样,第二个代码块(在花括号之间)是实际操作原始数组的代码块。
理解代码块的概念很重要,因为代码块对于理解yield功能至关重要。
收益如何运作…
请记住, yield允许正在执行的方法在其执行期间暂停,并且可以在完成原始方法之前执行另一个代码块中的指令。 让我们来看看它的作用。

不要让这个示例的度假主题欺骗您,我们仍在学习! 我们首先定义一个方法#travel ,该方法将3条语句输出到我们的控制台,并将s两次输出到给定的代码块。 (如果要在本地计算机上与这些示例一起进行编码,则代码位于文章的底部)
当我们在第10行调用#travel时,它会与相关的代码块一起调用。 首先,“开始度假的时间”将显示在屏幕上。 然后我们达到了第一个yield ; #travel方法将暂时暂停其执行,并且在第10行调用#travel时传递的代码块将接管: {| place | 放置“我们现在在#{place}! 它不漂亮吗?”}
因为我们在第4行和第6行中用参数调用了yield (即“ France”和“ Spain”),所以这些参数将作为位置变量插值到我们在第10行传递给#travel的块中。 ! 它会很漂亮吗?”将被打印到屏幕上,从而完成了我们第一个yield块的执行。 然后,我们返回到最初调用的#travel方法,该方法将显示“仍然行进”。然后循环浏览第二个yield块,最后再次返回#travel方法以完成该方法的执行。 运行代码时,输出为:
是时候开始我们的假期了。
我们现在在法国! 不漂亮吗?
仍在旅行。
我们现在在西班牙! 不漂亮吗?
假期结束了。
产量的多功能性
您现在已经看到了yield行动,但是一个简单的例子并不能完全发挥应有的作用。 因此,让我们修改上面的示例,以展示yield为开发人员提供的灵活性。

如您所见,我对原始示例所做的唯一修改是在第12行上添加了另一行代码。
第12行再次调用#travel方法,但它提供了一个与yield执行一起使用的备用代码块。 在这里,我们开始看到yield语句的真正多功能性。 如果现在运行此文件,我们将不仅具有原始输出,还将获得来自第12行的另一个包含不同块的第二次调用#travel的附加输出。 现在,我们的输出如下所示:
是时候开始我们的假期了。
我们现在在法国! 不漂亮吗?
仍在旅行。
我们现在在西班牙! 不漂亮吗?
假期结束了。
是时候开始我们的假期了。
法国...再次?!? 我去过
仍在旅行。
西班牙...再次?!? 我去过
假期结束了。
我们可以更改#travel方法的输出, #travel无需更改该方法内部的代码,只需更改#travel调用的块即可。
那就是yield的最终灵活性。 就像这位扮装皇后一样-能够以各种外观杀死跑道。 当扮装皇后改变衣服时, yield会根据调用包含yield的方法时经过的块而变化。
现在,根据#travel方法的编写方式,我们不能在没有块的情况下调用travel方法。 如果调用#travel ,则会出现错误:
是时候开始我们的假期了。
LocalJumpError:未提供任何块(产量)
我们的#travel方法开始执行; 实际上,该方法的第一行已执行并打印到屏幕上。 但是,一旦达到第一个yield ,我们的程序便会中断,因为yield预期会出现障碍,而没有给出yield 。 不用担心,这是我们可以解决的问题。 让我们调整原始方法,以包含条件if block_given? 。 使用yield时,这将提供最大的灵活性。

如您在上面的示例C中看到的, if block_given? 被添加到第4行和第6行。我们还在第14行调用#travel ,而#travel内的yield语句没有块。 现在,给定场景的输出为:
是时候开始我们的假期了。
我们现在在法国! 不漂亮吗?
仍在旅行。
我们现在在西班牙! 不漂亮吗?
假期结束了。
是时候开始我们的假期了。
法国...再次?!? 我去过
仍在旅行。
西班牙...再次?!? 我去过
假期结束了。
是时候开始我们的假期了。
仍在旅行。
假期结束了。
产量审查
要了解yield ,我们首先需要了解yield暂停当前正在执行的方法的基本概念。 暂停时, yield会传入我们调用原始方法时提供的单独代码块中。 执行与yield相关联的该附加代码块,然后程序返回到原始方法以完成该方法内部的任何其他代码。
Bala Paranj有一个很好的类比,可以帮助您阐明yield工作方式:
“当您开车时,如果看到了屈服信号,您可以让其他车辆通过后再进入道路。 在[编程语言]中,yield关键字将控制流产生到块中的代码。 因此,将执行该块中的代码,并在包含yield的行之后继续执行。”
通过yield将块传递给您的方法,使您可以稍微调整方法的功能,而无需编写全新的方法。
我们看到我们的#travel方法一次调用了一个块,然后再次调用了第二个代码块。 当将这两个不同的块打印到屏幕上时,我们的方法的结果在每种情况下都会改变。 而且我们能够做到这一点,而不必在我们的#travel方法中更改任何代码! 最后, if block_given?添加了if block_given? ,我们可以使用if block_given?的基本功能#travel无需传递块if block_given? 在每个yield声明之后。
在讨论了yield的目的并提供了一些示例之后,我希望您现在能够看到yield运算符的用途。 现在,继续并生成一些强大的灵活代码!
资料来源:
- RP James和A. Sabry。 “收益:主流定界延续”。 1983年出版.https://www.cs.indiana.edu/~sabry/papers/yield.pdf
- Bala Paranj。 “ Ruby基础知识:yield关键字”。 2017年3月.https://rubyplus.com/articles/4801-Ruby-Basics-The-yield-Keyword
如果您想在本地计算机上对其进行测试,请从上述旅行示例获取代码:
def旅行
输入“开始我们的假期的时间”。
如果block_given给出了“法国”?
放置“仍在旅行”。
如果block_given给出“西班牙”?
放置“休假结束”。
结束
旅行{|地点| 写道:“我们现在在#{place}!这不漂亮!”}
旅行{|地点| 再放“#{place} ... ??我已经去过。”}
旅行()