2011-09-07 44 views
2

我無法將嵌套For Generator生成一個For Generator。如何拼合使用I/O的嵌套For Comprehension?

我創建了MapSerializer來保存和加載地圖。上市

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在發電機內分配關鍵導致它失敗:

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) 

我想將加載代碼展平爲單一的理解,但是我得到的錯誤表明它要麼以不同的順序執行,要麼重複步驟我不期望它重複。

爲什麼將的賦值移到生成器中會導致它失敗?

我可以把它變成一個單一的發電機嗎?如果是這樣,該生成器將是什麼?

回答

6

謝謝你在自己的問題中包含編譯代碼。由於結構不平坦,我不認爲你想要平整這些環路。然後您需要使用groupBy來恢復結構。此外,如果您將「零 - > Seq()」作爲地圖的一個元素,它將會丟失。使用這種簡單的地圖避免了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循環和啓動使用作爲flatMap和地圖改寫:

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之前連續調用。

+0

+99爲優秀的'breakOut'使用案例,並張貼回覆時,我仍然在製作我自己的:) –

+0

@Kevin賴特,我希望我有更多的時間來弄清楚爲什麼flatMap/map工作,但for循環沒有。 – huynhjl

+0

@huynhjl太棒了。這工作,很優雅。謝謝:) –

1

這裏是@ huynhjl的回答的壓縮版本,我最終部署:

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

這個版本的優點是,有沒有直接的任務。

+1

風格上的小點:而不是'm.map {...}'建議使用中綴表示法'm map {...}'。這裏的[風格指南]部分(http://davetron5000.github.com/scala-style/method_invocation/arity1/higher_order_functions.html) –

+0

@基普頓巴羅斯:好點。固定。 –