Scala相当于Python生成器?

huy*_*hjl 55 python scala

是否有可能在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成语.

  • 谢谢,我认为Stream解决方案是我正在寻找的,因为它懒洋洋地评估.你是对的,我不想在Scala中编写python,但是因为在我没有遇到解决方案之前我从未使用过流. (2认同)

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)