yield随想

2022年9月3日

C#、Python程序中可以写yield指令,这样程序的执行权就暂时交出去了,等另一方运行完成后再回来。设当前方法为f,f可以集中处理资源创建和销毁,资源创建完成后,用yield指令把资源交给另一个方法。

C#实现如下。f为包含yield的方法,File类代表对外部资源的封装。f的方法体集中处理file.Open()和file.Close(),并在中间把可用的file传出去。在main方法中,通过某种结构调用f方法,获得了可用的file,并写出字符串。

class Program
{
	static void Main(string[] args)
	{
		foreach (File f in f())
		{
			f.WriteLine("hello");
			f.WriteLine("How are you?");
		}

	}

	static IEnumerable f()
	{
		var file = new File();
		file.Open();

		yield return file;

		file.Close();
	}


	class File { ... }
}

前面的例子浅显易懂,但是包含yield的方法这个名字有什么专业的说法吗?答案是coroutine(协程)。

Python的yeild指令更强大一点,它可以返回值。这个返回值是通过send命令传入coroutine。

def f():
	yield 'f1'
	s = yield 'f2'
	print(f'yield return {s}')
	yield 'f3'


if __name__ == '__main__':
	g = f()
	print(f'get {next(g)}')
	print(f'get {next(g)}')
	print(f'get {g.send("s2")}')

这个python程序的输出是

get f1
get f2
yield return s2
get f3

当coroutine交出执行权后,另一个程序优先执行。具体表现如下。yield ‘f1’执行后,一直执行main block,直到遇到下一个next(g)或g.send。接着,执行yield ‘f2’,注意不包括s=,直到遇到下一个next(g)或g.send。这里程序遇到g.send(“s2”)。然后执行返回至f,并运行s=,所以打印出yield return s2。

yield的数量

一个coroutine yield一次,可以实现using block;yield两次,实现try-catch block;yield三次,实现try-catch-finally block。

设coroutine为f,另一个方法为g,该程序不能写成f(g())。因为按照数学,必须先计算g,后计算f。