3

不変のマップを使用してスカラに単純なワードカウントを実装しようとしています(これは意図的なものです)。それを達成しようとしている方法は次のとおりです:

  1. 空の不変マップを作成する
  2. ファイルを読み取るスキャナーを作成します。
  3. scanner.hasNext() が true の場合:

    • マップに単語が含まれているかどうかを確認し、単語が含まれていない場合は、カウントをゼロに初期化します
    • key=word と value=count+1 で新しいエントリを作成します
    • 地図を更新する
  4. 反復の最後に、マップにすべての値が取り込まれます。

私のコードは次のとおりです。

val wordMap = Map.empty[String,Int]
val input = new java.util.scanner(new java.io.File("textfile.txt"))
while(input.hasNext()){
  val token = input.next()
  val currentCount = wordMap.getOrElse(token,0) + 1
  val wordMap = wordMap + (token,currentCount)
}

アイデアは、反復の最後に wordMap がすべての wordCounts を持つことです...このスニペットを実行しようとするたびに、次の例外が発生します

再帰値 wordMap には型が必要です。

この例外が発生する理由と、それを修正するために何ができるかを誰かが指摘できますか?

ありがとう

4

2 に答える 2

7
val wordMap = wordMap + (token,currentCount)

この行は、すでに定義されている変数を再定義しています。これを行いたい場合は、で定義wordMapしてから使用する必要がありvarます

wordMap = wordMap + (token,currentCount)

代わりにこれはどうですか?

io.Source.fromFile("textfile.txt")            // read from the file
  .getLines.flatMap{ line =>                  // for each line
     line.split("\\s+")                       // split the line into tokens
       .groupBy(identity).mapValues(_.size)   // count each token in the line
  }                                           // this produces an iterator of token counts
  .toStream                                   // make a Stream so we can groupBy
  .groupBy(_._1).mapValues(_.map(_._2).sum)   // combine all the per-line counts
  .toList

行ごとの事前集計は、必要なメモリを削減しようとするために使用されることに注意してください。ファイル全体を一度にカウントすると、大きすぎる場合があります。

ファイルが非常に大きい場合は、Scala の並列コレクションまたは Hadoop (Scrunch や Scoobi などのクールな Scala Hadoop ラッパーの 1 つを使用) を使用して、これを並列で実行することをお勧めします (単語カウントは並列化するのが簡単なので)。

編集:詳細な説明:

わかりました、まず flatMap の内部を見てください。文字列を取得し、空白で分割します。

val line = "a b c b"
val tokens = line.split("\\s+") // Array(a, b, c, a, b)

groupBy identity is a function that just returns its argument, so if we(identity)` では、それぞれの単語typeを各単語tokenにマッピングします。

val grouped = tokens.groupBy(identity) // Map(c -> Array(c), a -> Array(a), b -> Array(b, b))

そして最後に、各タイプのトークンの数を数えたいと思います:

val counts = grouped.mapValues(_.size) // Map(c -> 1, a -> 1, b -> 2)

これをファイル内のすべての行にマップするため、最終的には各行のトークン数になります。

それで、何をしflatMapますか?各行に対してトークン カウント関数を実行し、すべての結果を 1 つの大きなコレクションに結合します。

ファイルが次のとおりであると仮定します。

a b c b
b c d d d
e f c

次に、次のようになります。

val countsByLine = 
  io.Source.fromFile("textfile.txt")            // read from the file
    .getLines.flatMap{ line =>                  // for each line
       line.split("\\s+")                       // split the line into tokens
         .groupBy(identity).mapValues(_.size)   // count each token in the line
    }                                           // this produces an iterator of token counts
println(countsByLine.toList) // List((c,1), (a,1), (b,2), (c,1), (d,3), (b,1), (c,1), (e,1), (f,1))

そのため、各行のカウントを 1 つの大きなカウント セットに結合する必要があります。countsByLine変数は であるため、メソッドIteratorはありませんgroupByStream代わりに、基本的に遅延リストである に変換できます。開始する前にファイル全体をメモリに読み込む必要がないため、遅延が必要です。次に、groupBy同じ単語タイプのすべてのカウントをグループ化します。

val groupedCounts = countsByLine.toStream.groupBy(_._1)
println(groupedCounts.mapValues(_.toList)) // Map(e -> List((e,1)), f -> List((f,1)), a -> List((a,1)), b -> List((b,2), (b,1)), c -> List((c,1), (c,1), (c,1)), d -> List((d,3)))

そして最後に、各タプルから 2 番目の項目 (カウント) を取得して合計することで、各単語タイプの各行のカウントを合計できます。

val totalCounts = groupedCounts.mapValues(_.map(_._2).sum)
println(totalCounts.toList)
List((e,1), (f,1), (a,1), (b,3), (c,3), (d,3))

そして、あなたはそれを持っています。

于 2012-04-15T20:37:43.873 に答える
3

いくつかの間違いがあります: wordMap2 回定義しました (val値を宣言することです)。また、Mapは不変であるため、 として宣言するかvar、可変マップを使用する必要があります (前者をお勧めします)。

これを試して:

var wordMap = Map.empty[String,Int] withDefaultValue 0
val input = new java.util.Scanner(new java.io.File("textfile.txt"))
while(input.hasNext()){
  val token = input.next()
  wordMap += token -> (wordMap(token) + 1)
}
于 2012-04-15T20:37:13.123 に答える