Scala 相当于 Python 生成器? [英] Scala equivalent to Python generators?

查看:39
本文介绍了Scala 相当于 Python 生成器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否有可能在 Scala 中实现与 Python yield 语句等效的东西,它会记住使用它的函数的本地状态,并在每次调用时产生"下一个值?

Is it possible to implement in Scala something equivalent to the Python yield statement where it remembers the local state of the function where it is used and "yields" the next value each time it is called?

我想用这样的方法将递归函数转换为迭代器.有点像这样:

I wanted to have something like this to convert a recursive function into an iterator. Sort of like this:

# 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

除此之外,foo 可能更复杂,并通过一些非循环对象图递归.

Except, foo may be more complex and recurs through some acyclic object graph.

其他让我添加一个更复杂的例子(但仍然很简单):我可以编写一个简单的递归函数来打印内容:

Additional Let me add a more complex example (but still simple): I can write a simple recursive function printing things as it goes along:

// 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 + "  ")
      }
  }
}

理想情况下,我希望有一个库,可以让我轻松更改一些语句并将其用作迭代器:

Ideally I would like to have a library that allows me to easily change a few statements and have it work as an Iterator:

// 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
  }
}

似乎延续允许这样做,但我只是不理解 shift/reset 概念.延续最终会进入主编译器,是否有可能提取出库中的复杂性?

It does seem continuations allow to do that, but I just don't understand the shift/reset concept. Will continuation eventually make it into the main compiler and would it be possible to extract out the complexity in a library?

编辑 2:在另一个线程中查看 Rich 的回答.>

Edit 2: check Rich's answer in that other thread.

推荐答案

虽然 Python 生成器很酷,但在 Scala 中尝试复制它们确实不是最好的方法.例如,下面的代码完成了与您想要的等效的工作:

While Python generators are cool, trying to duplicate them really isn't the best way to go about in Scala. For instance, the following code does the equivalent job to what you want:

def classStream(clazz: Class[_]): Stream[Class[_]] = clazz match {
  case null => Stream.empty
  case _ => (
    clazz 
    #:: classStream(clazz.getSuperclass) 
    #::: clazz.getInterfaces.toStream.flatMap(classStream) 
    #::: Stream.empty
  )
}

其中的流是惰性生成的,因此在请求之前它不会处理任何元素,您可以通过运行以下命令进行验证:

In it the stream is generated lazily, so it won't process any of the elements until asked for, which you can verify by running this:

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
  )
}

只需在结果 Stream 上调用 .iterator 即可将结果转换为 Iterator:

The result can be converted into an Iterator simply by calling .iterator on the resulting Stream:

def classIterator(clazz: Class[_]): Iterator[Class[_]] = classStream(clazz).iterator

使用 Streamfoo 定义将被渲染为:

The foo definition, using Stream, would be rendered thus:

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

另一种选择是连接各种迭代器,注意不要预先计算它们.这是一个示例,还包含调试消息以帮助跟踪执行:

Another alternative would be concatenating the various iterators, taking care to not pre-compute them. Here's an example, also with debugging messages to help trace the execution:

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
}

这与您的代码非常接近.我有定义,而不是 sudoYield,然后我就按照自己的意愿将它们连接起来.

This is pretty close to your code. Instead of sudoYield, I have definitions, and then I just concatenate them as I wish.

所以,虽然这是一个非答案,但我只是认为你在这里吠错了树.尝试在 Scala 中编写 Python 注定是徒劳的.在实现相同目标的 Scala 习语上更加努力.

So, while this is a non-answer, I just think you are barking up the wrong tree here. Trying to write Python in Scala is bound to be unproductive. Work harder at the Scala idioms that accomplish the same goals.

这篇关于Scala 相当于 Python 生成器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆