生成器基本上是具有一些令人讨厌的限制的半协程。因此,显然,您可以使用半协程(当然还有全协程)来实现它们。
如果您没有协程,则可以使用任何其他通用控制流构造。有很多控制流构造是“通用的”,因为每个控制流构造(包括所有其他通用控制流构造),包括协程,因此生成器可以(或多或少)简单地转换为只有通用的构造。
其中最著名的可能是GOTO
. 使用 just GOTO
,您可以构建任何其他控制流构造: 、、、、、、异常IF-THEN-ELSE
、线程WHILE
、子例程调用FOR
、方法调用REPEAT-UNTIL
、FOREACH
函数调用等,当然还有协程和生成器。
几乎所有 CPU 都支持GOTO
(尽管在 CPU 中,他们通常称它为jmp
)。事实上,在许多 CPU 中,GOTO
它是唯一call
的控制流构造,尽管今天通常还内置了对至少子例程调用 ( .
另一个众所周知的控制流原语是延续。Continuations 基本上是一种更结构化、更易于管理且不那么邪恶的变体GOTO
,在函数式语言中尤其流行。但也有一些低级语言将其控制流基于延续,例如 Parrot 虚拟机使用延续来控制流,我相信在某个研究实验室的某个地方甚至有一些基于延续的 CPU。
C 有一种“蹩脚”形式的延续 ( setjmp
and longjmp
),它比“真正的”延续要弱得多,也更不容易使用,但它们足够强大,可以实现生成器(事实上,可以用来实现完整的延续)。
在 Unix 平台上,setcontext
可以用作setjmp
/的更强大和更高级别的替代品longjmp
。
另一个众所周知的控制流构造,但可能不会想到作为低级基板构建其他控制流构造之上,是例外。有一篇论文表明异常可以比延续更强大,从而使异常本质上等同于GOTO
并因此具有普遍的强大功能。而且,事实上,异常有时被用作通用控制流结构:Microsoft Volta 项目将 .NET 字节码编译为 JavaScript,使用 JavaScript 异常来实现 .NET 线程和生成器。
不是通用的,但可能足够强大以实现生成器只是简单的尾调用优化。(不过,我可能错了。不幸的是,我没有证据。)我认为您可以将生成器转换为一组相互尾递归的函数。我知道状态机可以使用尾调用来实现,所以我很确定生成器也可以,因为毕竟 C# 将生成器实现为状态机。(我认为这与惰性评估一起使用特别好。)
最后但同样重要的是,在具有具体调用堆栈的语言中(例如大多数 Smalltalks),您可以构建几乎任何类型的控制流结构。(事实上,一个具体化的调用堆栈基本上是相当于功能性高级延续的过程低级。)
那么,生成器的其他实现是什么样的呢?
Lua 本身没有生成器,但它有完整的非对称协程。主要的 C 实现使用setjmp
/longjmp
来实现它们。
Ruby 本身也没有生成器,但它有Enumerator
s,可以用作生成器。Enumerator
s 不是语言的一部分,它们是库功能。MRIEnumerator
使用延续来实现 s,而延续又使用setjmp
/来实现longjmp
。YARVEnumerator
使用s 实现Fiber
s (这是 Ruby 拼写“协程”的方式),而那些是使用setjmp
/实现的longjmp
。我相信 JRuby 目前Enumerator
使用线程实现 s,但是一旦 JVM 获得一些更好的控制流构造,他们希望切换到更好的东西。
Python 的生成器实际上或多或少是成熟的协程。CPython 使用setjmp
/实现它们longjmp
。