是否有可能在Scala中实现与Python yield语句等效的东西,它会记住使用它的函数的本地状态,并在每次调用时"产生"下一个值?
我希望有这样的东西将递归函数转换为迭代器.有点像:
# this is python
def foo(i):
yield i
if i > 0:
for j in foo(i - 1):
yield j
for i in foo(5):
print i
Run Code Online (Sandbox Code Playgroud)
除此之外,foo可能更复杂并通过一些非循环对象图重现.
附加编辑: 让我添加一个更复杂的例子(但仍然很简单):我可以编写一个简单的递归函数来打印事物:
// this is Scala
def printClass(clazz:Class[_], indent:String=""): Unit = {
clazz match {
case null =>
case _ =>
println(indent + clazz)
printClass(clazz.getSuperclass, indent + " ")
for (c <- clazz.getInterfaces) {
printClass(c, indent + " ")
}
}
}
Run Code Online (Sandbox Code Playgroud)
理想情况下,我希望有一个库,允许我轻松更改一些语句并使其作为迭代器工作:
// this is not Scala
def yieldClass(clazz:Class[_]): Iterator[Class[_]] = {
clazz match {
case null =>
case _ =>
sudoYield clazz
for (c <- yieldClass(clazz.getSuperclass)) sudoYield c
for (c <- clazz.getInterfaces; d <- yieldClasss(c)) sudoYield d
}
}
Run Code Online (Sandbox Code Playgroud)
似乎延续允许这样做,但我只是不理解这个shift/reset概念.继续最终是否会成为主编译器,是否有可能提取库中的复杂性?
编辑2:在另一个帖子中 检查Rich的答案.
Dan*_*ral 34
虽然Python生成器很酷,但尝试复制它们确实不是Scala中最好的方法.例如,以下代码执行与您想要的相同的工作:
def classStream(clazz: Class[_]): Stream[Class[_]] = clazz match {
case null => Stream.empty
case _ => (
clazz
#:: classStream(clazz.getSuperclass)
#::: clazz.getInterfaces.toStream.flatMap(classStream)
#::: Stream.empty
)
}
Run Code Online (Sandbox Code Playgroud)
在其中流是懒惰生成的,因此在询问之前它不会处理任何元素,您可以通过运行以下方法来验证:
def classStream(clazz: Class[_]): Stream[Class[_]] = clazz match {
case null => Stream.empty
case _ => (
clazz
#:: { println(clazz.toString+": super"); classStream(clazz.getSuperclass) }
#::: { println(clazz.toString+": interfaces"); clazz.getInterfaces.toStream.flatMap(classStream) }
#::: Stream.empty
)
}
Run Code Online (Sandbox Code Playgroud)
结果可以被转换成Iterator简单地通过调用.iterator对所得Stream:
def classIterator(clazz: Class[_]): Iterator[Class[_]] = classStream(clazz).iterator
Run Code Online (Sandbox Code Playgroud)
因此foo,使用的定义Stream将被呈现:
scala> def foo(i: Int): Stream[Int] = i #:: (if (i > 0) foo(i - 1) else Stream.empty)
foo: (i: Int)Stream[Int]
scala> foo(5) foreach println
5
4
3
2
1
0
Run Code Online (Sandbox Code Playgroud)
另一种方法是连接各种迭代器,注意不要预先计算它们.这是一个示例,还有调试消息来帮助跟踪执行:
def yieldClass(clazz: Class[_]): Iterator[Class[_]] = clazz match {
case null => println("empty"); Iterator.empty
case _ =>
def thisIterator = { println("self of "+clazz); Iterator(clazz) }
def superIterator = { println("super of "+clazz); yieldClass(clazz.getSuperclass) }
def interfacesIterator = { println("interfaces of "+clazz); clazz.getInterfaces.iterator flatMap yieldClass }
thisIterator ++ superIterator ++ interfacesIterator
}
Run Code Online (Sandbox Code Playgroud)
这非常接近您的代码.而不是sudoYield,我有定义,然后我只是按照我的意愿连接它们.
所以,虽然这不是一个答案,但我只是认为你在这里咆哮错误的树.尝试在Scala中编写Python肯定是徒劳的.努力实现同样目标的Scala成语.
Mil*_*bin 11
另一种基于插件的延续解决方案,这次使用或多或少封装的Generator类型,
import scala.continuations._
import scala.continuations.ControlContext._
object Test {
def loopWhile(cond: =>Boolean)(body: =>(Unit @suspendable)): Unit @suspendable = {
if (cond) {
body
loopWhile(cond)(body)
} else ()
}
abstract class Generator[T] {
var producerCont : (Unit => Unit) = null
var consumerCont : (T => Unit) = null
protected def body : Unit @suspendable
reset {
body
}
def generate(t : T) : Unit @suspendable =
shift {
(k : Unit => Unit) => {
producerCont = k
if (consumerCont != null)
consumerCont(t)
}
}
def next : T @suspendable =
shift {
(k : T => Unit) => {
consumerCont = k
if (producerCont != null)
producerCont()
}
}
}
def main(args: Array[String]) {
val g = new Generator[Int] {
def body = {
var i = 0
loopWhile(i < 10) {
generate(i)
i += 1
}
}
}
reset {
loopWhile(true) {
println("Generated: "+g.next)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
17346 次 |
| 最近记录: |