Rog*_*son 3 dsl yield generator
我对语言设计有点迷茫,我现在正在玩自己的爱好语言.(http://rogeralsing.com/2010/04/14/playing-with-plastic/)
让我头脑流血的一件事是"生成器"和"yield"关键字.我知道C#使用AST转换将枚举器方法转换为状态机.
但它在其他语言中如何运作?有没有办法在没有AST转换的语言中获得生成器支持?例如,像Python或Ruby这样的语言是否采用AST转换来解决这个问题?
(问题是如何在不同语言的引擎下实现生成器,而不是如何在其中一个中编写生成器)
生成器基本上是半协同程序,有一些恼人的限制.所以,显然,你可以使用半协同程序(当然还有完整的协同程序)来实现它们.
如果您没有协程,则可以使用任何其他通用控制流构造.在某种意义上,有许多控制流构造是"通用的",即每个控制流构造(包括所有其他通用控制流构造),包括协同程序和因此生成器可以(或多或少)简单地转换为只有通用构造.
其中最着名的可能是GOTO.只需GOTO,您可以构建任何其他控制流结构:IF-THEN-ELSE,WHILE,FOR,REPEAT-UNTIL,FOREACH,异常,线程,子程序调用,方法调用,函数调用等等,当然还协同程序和发电机.
几乎所有CPU都支持GOTO(尽管在CPU中,它们通常称之为jmp).事实上,在许多CPU中,GOTO是唯一的控制流构造,尽管今天至少对子程序调用(call)和可能是一些原始形式的异常处理和/或并发原语(比较和交换)的本机支持通常也是内置的.
另一个众所周知的控制流原语是延续.Continuations基本上是一个更结构化,更易于管理和更不邪恶的变体GOTO,特别是在函数式语言中很流行.但也有一些低级语言将其控制流基于延续,例如Parrot虚拟机使用控制流的延续,我相信在某些研究实验室中甚至还有一些基于延续的CPU.
C有一种"糟糕"的延续形式(setjmp和longjmp),它比"真正的"延续功能强大得多且不易使用,但是它们足够强大以实现生成器(事实上,可以用来实施完整的延续).
在Unix平台,setcontext可以作为一个功能更强大的和更高水平的替代setjmp/ longjmp.
另一种众所周知的控制流结构是例外,但它可能不会引起人们的注意,因为低级基板构建了其他控制流结构.有一篇论文表明,异常可以比延续更强大,从而使异常基本等同于GOTO并因此具有普遍性.而且,事实上,例外是有时用作通用控制流结构:微软沃尔特项目,编译.NET字节码为JavaScript,使用JavaScript异常实现.NET线程和发电机.
不是通用的,但可能强大到足以实现生成器只是简单的尾部调用优化.(但我可能错了.遗憾的是我没有证据.)我认为你可以将生成器转换为一组相互尾递归的函数.我知道状态机可以使用尾调用来实现,所以我很确定生成器也可以,因为毕竟C#将生成器实现为状态机.(我认为这与懒惰评估一起工作特别好.)
最后但并非最不重要的是,在一个带有reified调用堆栈的语言中(例如大多数Smalltalks),你可以构建你想要的任何类型的控制流构造.(实际上,一个具体化的调用堆栈基本上是与功能性高级延续相当的程序性低级别.)
那么,是什么做发电机的其他实现方式是什么样子?
Lua本身没有发生器,但它具有完全不对称的协同程序.主要的C实现使用setjmp/ longjmp来实现它们.
Ruby本身也没有生成器,但它有Enumerators,可以用作生成器.Enumerators不是语言的一部分,它们是库功能.MRI Enumerator使用continuation 实现,而continuation又使用setjmp/ 来实现longjmp.YARV实现了Enumerators使用Fibers(这就是Ruby拼写"coroutines"的方式),并且这些是使用setjmp/ 实现的longjmp.我相信JRuby目前Enumerator使用线程来实现,但是一旦JVM获得更好的控制流结构,他们就想要切换到更好的东西.
Python的生成器实际上或多或少都是完整的协同程序.CPython使用setjmp/ 实现它们longjmp.