IT TIP

Python 생성기와 동일한 Scala?

itqueen 2021. 1. 8. 22:38
반응형

Python 생성기와 동일한 Scala?


yield사용되는 함수의 로컬 상태를 기억하고 호출 될 때마다 다음 값을 "수율"하는 Python 문과 동일한 것을 Scala에서 구현할 수 있습니까?

재귀 함수를 반복자로 변환하기 위해 이와 같은 것을 갖고 싶었습니다. 다음과 같습니다.

# 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의 대답확인하십시오 .


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정의 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

또 다른 대안은 미리 계산하지 않도록주의하면서 다양한 반복자를 연결하는 것입니다. 다음은 실행을 추적하는 데 도움이되는 디버깅 메시지가 포함 된 예입니다.

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 관용구에서 더 열심히 일하십시오.


이번에는 다소 캡슐화 된 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)
      }
    }
  }
}

일반적인 방법으로이를 수행하려면 continuations 플러그인 이 필요하다고 생각합니다 .

순진한 구현 (자유, 컴파일 / 확인되지 않음) :

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변수 에 저장합니다 . 그것은 hasNextnext메소드에 의해 외부에서 구동됩니다 .

진정해, 이것은 분명히 이것을 구현하는 끔찍한 방법입니다. 그러나 편리한 컴파일러없이 밤늦게까지 할 수있는 것이 가장 좋습니다.


Scala의 for-loop 형식 / 로 직접 변환되지 않고 호출 for (e <- Producer) f(e)변환됩니다 .foreachiteratornext

에서 foreach우리는 객체의 생성을 선형화 할 필요가 없으며 반복자의 next. 소비자 함수 f는 정확히 필요한 곳에 (즉, 객체가 생성되는) 여러 번 삽입 될 수 있습니다.

이것은 Scala에서 Traversable/ 를 사용하여 생성기에 대한 사용 사례를 간단하고 효율적으로 구현 foreach합니다.


초기 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

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

Other examples can be found in the Scaladoc.

ReferenceURL : https://stackoverflow.com/questions/2137619/scala-equivalent-to-python-generators

반응형