scala - 如何展平使用 I/O 的嵌套 For Comprehension?

标签 scala for-loop scala-collections

我无法将嵌套的 For 生成器展平为单个 For 生成器。

我创建了 MapSerializer 来保存和加载 map 。

MapSerializer.scala 列表:

import java.io.{ObjectInputStream, ObjectOutputStream}

object MapSerializer {
  def loadMap(in: ObjectInputStream): Map[String, IndexedSeq[Int]] =
    (for (_ <- 1 to in.readInt()) yield {
      val key = in.readUTF()
      for (_ <- 1 to in.readInt()) yield {
        val value = in.readInt()
        (key, value)
      }
    }).flatten.groupBy(_ _1).mapValues(_ map(_ _2))

  def saveMap(out: ObjectOutputStream, map: Map[String, Seq[Int]]) {
    out.writeInt(map size)
    for ((key, values) <- map) {
      out.writeUTF(key)
      out.writeInt(values size)
      values.foreach(out.writeInt(_))
    }
  }
}

修改 loadMap 以在生成器中分配 key 会导致它失败:

def loadMap(in: ObjectInputStream): Map[String, IndexedSeq[Int]] =
  (for (_ <- 1 to in.readInt();
        key = in.readUTF()) yield {
    for (_ <- 1 to in.readInt()) yield {
      val value = in.readInt()
      (key, value)
    }
  }).flatten.groupBy(_ _1).mapValues(_ map(_ _2))

这是我得到的堆栈跟踪:

java.io.UTFDataFormatException
    at java.io.ObjectInputStream$BlockDataInputStream.readWholeUTFSpan(ObjectInputStream.java)
    at java.io.ObjectInputStream$BlockDataInputStream.readOpUTFSpan(ObjectInputStream.java)
    at java.io.ObjectInputStream$BlockDataInputStream.readWholeUTFSpan(ObjectInputStream.java)
    at java.io.ObjectInputStream$BlockDataInputStream.readUTFBody(ObjectInputStream.java)
    at java.io.ObjectInputStream$BlockDataInputStream.readUTF(ObjectInputStream.java:2819)
    at java.io.ObjectInputStream.readUTF(ObjectInputStream.java:1050)
    at MapSerializer$$anonfun$loadMap$1.apply(MapSerializer.scala:8)
    at MapSerializer$$anonfun$loadMap$1.apply(MapSerializer.scala:7)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:194)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:194)
    at scala.collection.immutable.Range.foreach(Range.scala:76)
    at scala.collection.TraversableLike$class.map(TraversableLike.scala:194)
    at scala.collection.immutable.Range.map(Range.scala:43)
    at MapSerializer$.loadMap(MapSerializer.scala:7)

我想将加载代码扁平化为单个 For Comprehension,但我得到的错误表明它正在以不同的顺序执行或重复我不希望它重复的步骤。

为什么将 key 的赋值移动到生成器中会导致它失败?

我可以将其扁平化为一个生成器吗?如果是这样,那台发电机会是什么?

最佳答案

感谢您在问题中提供独立的编译代码。我不认为你想压平循环,因为结构不平坦。然后您需要使用 groupBy 来恢复结构。此外,如果您将“零 -> Seq()”作为 map 的元素,它将丢失。使用这个简单的映射可以避免 groupBy 并保留映射到空序列的元素:

def loadMap(in: ObjectInputStream): Map[String, IndexedSeq[Int]] = {
  val size = in.readInt
  (1 to size).map{ _ =>
    val key = in.readUTF
    val nval = in.readInt
    key -> (1 to nval).map(_ => in.readInt)
  }(collection.breakOut)
}

我使用 breakOut 来生成正确的类型,否则我认为编译器会提示通用 Map 和不可变的 Map 不匹配。您还可以使用 Map()++ (...)

注意:我对您的 for 循环感到困惑,并开始使用 as flatMap 和 map 重写,从而得出了这个解决方案:

val tuples = (1 to size).flatMap{ _ =>
  val key = in.readUTF
  println("key " + key)
  val nval = in.readInt
  (1 to nval).map(_ => key -> in.readInt)
}

我认为在 for 循环中,当您不使用某些生成器时会发生一些事情。我认为这相当于:

val tuples = for {
  _ <- 1 to size
  key = in.readUTF
  nval = in.readInt
  _ <- 1 to nval
  value = in.readInt
} yield { key -> value }

但事实并非如此,所以我认为我在翻译中遗漏了一些东西。

编辑:找出单个 for 循环的问题所在。简短的故事:for 循环中定义的翻译导致 key = in.readUTF 语句在执行内部循环之前被连续调用。要解决此问题,请使用 viewforce:

val tuples = (for {
  _ <- (1 to size).view
  key = in.readUTF
  nval = in.readInt
  _ <- 1 to nval
  value = in.readInt
} yield { key -> value }).force

这段代码可以更清楚地说明问题:

val iter = Iterator.from(1)
val tuple = for {
  _ <- 1 to 3
  outer = iter.next
  _ <- 1 to 3
  inner = iter.next
} yield (outer, inner)

它返回 Vector((1,4), (1,5), (1,6), (2,7), (2,8), (2,9), (3,10 ), (3,11), (3,12)) 这表明所有外部值都先于内部值求值。这是因为它或多或少是translated。类似于:

for { 
  (i, outer) <- for (i <- (1 to 3)) yield (i, iter.next)
  _ <- 1 to 3
 inner = iter.next
} yield (outer, inner)

这首先计算所有外部 iter.next。回到最初的用例,所有 in.readUTF 值将在 in.readInt 之前被连续调用。

关于scala - 如何展平使用 I/O 的嵌套 For Comprehension?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7334572/

相关文章:

ios - 延迟循环(倒计时功能)

arrays - 如果 Scala 中需要不可变数组,返回 IndexesSeq 而不是 Array 是否正确?

scala - 如何在 Scala 中创建一组字符?

java - ZonedDateTime.parse 错误?

scala - 如何附加 Scala Intellij 调试器进行测试?

python - 向量化嵌套 for 循环

c++ - C++ 的其他循环选项

scala - 采用隐式 CanBuildFrom 的方法不适用于 eta-expansion?

java - 如何在类编译后运行java程序

scala - 检查 var 是 Scala 中的哪个匿名函数