我无法将嵌套的 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
语句在执行内部循环之前被连续调用。要解决此问题,请使用 view
和 force
:
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/