代码之家  ›  专栏  ›  技术社区  ›  huynhjl bhericher

scala相当于python生成器?

  •  51
  • huynhjl bhericher  · 技术社区  · 14 年前

    是否可以在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
    

    除了, 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 + "  ")
          }
      }
    }
    

    理想情况下,我希望有一个库,允许我轻松地更改一些语句,并让它作为迭代器工作:

    // 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 概念。延续最终会使它成为主编译器吗?是否可以从库中提取出复杂性?

    编辑2: 检查 Rich's answer 在另一条线上。

    5 回复  |  直到 6 年前
        1
  •  32
  •   Nathaniel Ford    8 年前

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

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

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

    结果可以转换为 Iterator 只需打电话 .iterator 关于结果 Stream :

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

    这个 foo 定义,使用 河流 ,将这样呈现:

    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
    

    另一种选择是连接各种迭代器,注意不要预先计算它们。下面是一个示例,其中还包括用于帮助跟踪执行的调试消息:

    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 ,我有定义,然后根据需要连接它们。

    所以,虽然这不是答案,但我只是觉得你找错人了。尝试在scala中编写python肯定是徒劳的。在完成相同目标的scala习惯用法上更加努力。

        2
  •  11
  •   Miles Sabin    14 年前

    另一个基于插件的延续解决方案,这次使用或多或少封装的生成器类型,

    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)
          }
        }
      }
    }
    
        3
  •  3
  •   Mitch Blevins    14 年前

    要以一般的方式做到这一点,我认为你需要 continuations plugin .

    简单的实现(徒手,未编译/检查):

    def iterator = new {
      private[this] var done = false
    
      // Define your yielding state here
      // This generator yields: 3, 13, 0, 1, 3, 6, 26, 27
      private[this] var state: Unit=>Int = reset {
        var x = 3
        giveItUp(x)
        x += 10
        giveItUp(x)
        x = 0
        giveItUp(x)
        List(1,2,3).foreach { i => x += i; giveItUp(x) }
        x += 20
        giveItUp(x)
        x += 1
        done = true
        x
      }
    
      // Well, "yield" is a keyword, so how about giveItUp?
      private[this] def giveItUp(i: Int) = shift { k: (Unit=>Int) =>
        state = k
        i
      }
    
      def hasNext = !done
      def next = state()
    }
    

    发生什么事了 shift 捕获从调用位置到 reset 阻止它被调用。这被当作 k 移位函数中的参数。

    因此,在上面的例子中,每个 giveItUp(x) 返回的值 x (到此为止)并将其余计算保存在 state 变量。它是由外部驱动的 hasNext next 方法。

    温和一点,这显然是一个糟糕的实现方法。但我最好在晚上晚些时候不用编译器。

        4
  •  3
  •   Eugen Labun rbrayb    13 年前

    窗体循环的scala for (e <- Producer) f(e) 翻译成 foreach 呼叫,而不是直接呼叫 iterator / next .

    前额 我们不需要线性化对象的创建并将它们放在一个地方,因为迭代器需要这样做。 下一个 . 消费者职能 f 可以多次插入,精确到需要插入的位置(即创建对象的位置)。

    这使得为生成器实现用例变得简单而高效 Traversable / 前额 在斯卡拉。


    初始foo示例:

    case class Countdown(start: Int) extends Traversable[Int] {
        def foreach[U](f: Int => U) {
            var j = start
            while (j >= 0) {f(j); j -= 1}
        }
    }
    
    for (i <- Countdown(5))  println(i)
    // or equivalent:
    Countdown(5) foreach println
    

    初始printclass示例:

      // v1 (without indentation)
    
      case class ClassStructure(c: Class[_]) {
        def foreach[U](f: Class[_] => U) {
          if (c eq null) return
          f(c)
          ClassStructure(c.getSuperclass) foreach f
          c.getInterfaces foreach (ClassStructure(_) foreach f)
        }
      }
    
      for (c <- ClassStructure(<foo/>.getClass)) println(c)
      // or equivalent:
      ClassStructure(<foo/>.getClass) foreach println
    

    或有压痕:

      // v2 (with indentation)
    
      case class ClassWithIndent(c: Class[_], indent: String = "") {
        override def toString = indent + c
      }
      implicit def Class2WithIndent(c: Class[_]) = ClassWithIndent(c)
    
      case class ClassStructure(cwi: ClassWithIndent) {
        def foreach[U](f: ClassWithIndent => U) {
          if (cwi.c eq null) return
          f(cwi)
          ClassStructure(ClassWithIndent(cwi.c.getSuperclass, cwi.indent + "  ")) foreach f
          cwi.c.getInterfaces foreach (i => ClassStructure(ClassWithIndent(i, cwi.indent + "  ")) foreach f)
        }
      }
    
      for (c <- ClassStructure(<foo/>.getClass)) println(c)
      // or equivalent:
      ClassStructure(<foo/>.getClass) foreach println
    

    输出:

    class scala.xml.Elem
      class scala.xml.Node
        class scala.xml.NodeSeq
          class java.lang.Object
          interface scala.collection.immutable.Seq
            interface scala.collection.immutable.Iterable
              interface scala.collection.immutable.Traversable
                interface scala.collection.Traversable
                  interface scala.collection.TraversableLike
                    interface scala.collection.generic.HasNewBuilder
                    interface scala.collection.generic.FilterMonadic
                    interface scala.collection.TraversableOnce
                      interface scala.ScalaObject
                    interface scala.ScalaObject
                  interface scala.collection.generic.GenericTraversableTemplate
                    interface scala.collection.generic.HasNewBuilder
                    interface scala.ScalaObject
                  interface scala.ScalaObject
                interface scala.collection.generic.GenericTraversableTemplate
                  interface scala.collection.generic.HasNewBuilder
                  interface scala.ScalaObject
                interface scala.collection.TraversableLike
                  interface scala.collection.generic.HasNewBuilder
                  interface scala.collection.generic.FilterMonadic
                  interface scala.collection.TraversableOnce
                    interface scala.ScalaObject
                  interface scala.ScalaObject
                interface scala.Immutable
                interface scala.ScalaObject
              interface scala.collection.Iterable
                interface scala.collection.Traversable
                  interface scala.collection.TraversableLike
                    interface scala.collection.generic.HasNewBuilder
                    interface scala.collection.generic.FilterMonadic
                    interface scala.collection.TraversableOnce
                      interface scala.ScalaObject
                    interface scala.ScalaObject
                  interface scala.collection.generic.GenericTraversableTemplate
                    interface scala.collection.generic.HasNewBuilder
                    interface scala.ScalaObject
                  interface scala.ScalaObject
                interface scala.collection.generic.GenericTraversableTemplate
                  interface scala.collection.generic.HasNewBuilder
                  interface scala.ScalaObject
                interface scala.collection.IterableLike
                  interface scala.Equals
                  interface scala.collection.TraversableLike
                    interface scala.collection.generic.HasNewBuilder
                    interface scala.collection.generic.FilterMonadic
                    interface scala.collection.TraversableOnce
                      interface scala.ScalaObject
                    interface scala.ScalaObject
                  interface scala.ScalaObject
                interface scala.ScalaObject
              interface scala.collection.generic.GenericTraversableTemplate
                interface scala.collection.generic.HasNewBuilder
                interface scala.ScalaObject
              interface scala.collection.IterableLike
                interface scala.Equals
                interface scala.collection.TraversableLike
                  interface scala.collection.generic.HasNewBuilder
                  interface scala.collection.generic.FilterMonadic
                  interface scala.collection.TraversableOnce
                    interface scala.ScalaObject
                  interface scala.ScalaObject
                interface scala.ScalaObject
              interface scala.ScalaObject
            interface scala.collection.Seq
              interface scala.PartialFunction
                interface scala.Function1
                  interface scala.ScalaObject
                interface scala.ScalaObject
              interface scala.collection.Iterable
                interface scala.collection.Traversable
                  interface scala.collection.TraversableLike
                    interface scala.collection.generic.HasNewBuilder
                    interface scala.collection.generic.FilterMonadic
                    interface scala.collection.TraversableOnce
                      interface scala.ScalaObject
                    interface scala.ScalaObject
                  interface scala.collection.generic.GenericTraversableTemplate
                    interface scala.collection.generic.HasNewBuilder
                    interface scala.ScalaObject
                  interface scala.ScalaObject
                interface scala.collection.generic.GenericTraversableTemplate
                  interface scala.collection.generic.HasNewBuilder
                  interface scala.ScalaObject
                interface scala.collection.IterableLike
                  interface scala.Equals
                  interface scala.collection.TraversableLike
                    interface scala.collection.generic.HasNewBuilder
                    interface scala.collection.generic.FilterMonadic
                    interface scala.collection.TraversableOnce
                      interface scala.ScalaObject
                    interface scala.ScalaObject
                  interface scala.ScalaObject
                interface scala.ScalaObject
              interface scala.collection.generic.GenericTraversableTemplate
                interface scala.collection.generic.HasNewBuilder
                interface scala.ScalaObject
              interface scala.collection.SeqLike
                interface scala.collection.IterableLike
                  interface scala.Equals
                  interface scala.collection.TraversableLike
                    interface scala.collection.generic.HasNewBuilder
                    interface scala.collection.generic.FilterMonadic
                    interface scala.collection.TraversableOnce
                      interface scala.ScalaObject
                    interface scala.ScalaObject
                  interface scala.ScalaObject
                interface scala.ScalaObject
              interface scala.ScalaObject
            interface scala.collection.generic.GenericTraversableTemplate
              interface scala.collection.generic.HasNewBuilder
              interface scala.ScalaObject
            interface scala.collection.SeqLike
              interface scala.collection.IterableLike
                interface scala.Equals
                interface scala.collection.TraversableLike
                  interface scala.collection.generic.HasNewBuilder
                  interface scala.collection.generic.FilterMonadic
                  interface scala.collection.TraversableOnce
                    interface scala.ScalaObject
                  interface scala.ScalaObject
                interface scala.ScalaObject
              interface scala.ScalaObject
            interface scala.ScalaObject
          interface scala.collection.SeqLike
            interface scala.collection.IterableLike
              interface scala.Equals
              interface scala.collection.TraversableLike
                interface scala.collection.generic.HasNewBuilder
                interface scala.collection.generic.FilterMonadic
                interface scala.collection.TraversableOnce
                  interface scala.ScalaObject
                interface scala.ScalaObject
              interface scala.ScalaObject
            interface scala.ScalaObject
          interface scala.xml.Equality
            interface scala.Equals
            interface scala.ScalaObject
          interface scala.ScalaObject
        interface scala.ScalaObject
      interface scala.ScalaObject
      interface java.io.Serializable
    
        5
  •  0
  •   Yang Bo    6 年前

    Dsl.scala 就是你要找的。

    假设您想要创建一个随机数生成器。生成的数字应该存储在一个延迟计算的无限流中,该流可以借助于内置的特定于域的关键字进行构建。 Yield .

    import com.thoughtworks.dsl.keys.Yield
    def xorshiftRandomGenerator(seed: Int): Stream[Int] = {
      val tmp1 = seed ^ (seed << 13)
      val tmp2 = tmp1 ^ (tmp1 >>> 17)
      val tmp3 = tmp2 ^ (tmp2 << 5)
      !Yield(tmp3)
      xorshiftRandomGenerator(tmp3)
    }
    

    其他示例可在 Scaladoc .