2010-11-25 7 views
1

私は簡単なScalaコードを持っています。私は逐次List of Stringsをループし、リストrのタプル(String、Int)として収集する各Stringの出現を数えたいと思います。メイン関数の部分は残る必要があります(したがって、groupByなど)。私の質問は、更新機能についてです:このScalaフラグメントで1ループでオカレンスをカウントできますか?

今私はfindをまず実行し、存在しなければrに新しいタプルを追加します。それが存在する場合、私はrをループし、一致するStringのカウンタを更新します。

更新機能をより効率的に変更することはできますか? 1回の反復で更新できます(存在しない場合は追加、存在する場合はカウンタを更新します)。私はあなたが機能的なスタイルのために行くとScalaのコレクションの電源を使用することをお勧め

おかげ

var r = List[(String, Int)]() // (string, count) 

def update(s: String, l: List[(String, Int)]) : List[(String, Int)] = { 
    if (r.find(a => a._1 == s) == None) { 
    (s, 1) :: r // add a new item if it does not exist 
    } else { 
    for (b <- l) yield { 
     if (b._1 == s) { 
     (b._1, b._2 + 1) // update counter if exists 
     } else { 
     b // just yield if no match 
     } 
    } 
    } 
} 

def main(args : Array[String]) : Unit = { 
    val l = "A" :: "B" :: "A" :: "C" :: "A" :: "B" :: Nil 

    for (s <- l) r = update(s, r) 

    r foreach println 
} 
+1

私はあなたの 'groupBy'に対するあなたの反対を正確に理解していません。 'main'定義のコードは解決策によって変更されないかもしれないと言われています。 'l'が変更されないかもしれないことを意味するかもしれません。だから、あなたは明確にしてもらえますか? –

+0

私はまた、レイジーコレクションで作業したいからです。または、怠惰なコレクションや 'Streams'(または' Source.getLines')に 'groupBy'を使うこともできますか?もしそうなら、私に例を挙げてください。 –

答えて

4

あなたはGROUPBYを使用したり、怠惰なコレクションでは動作しない場合(ストリーム)は、行く方法かもしれない:

ss.foldLeft(Map[String, Int]())((m, s) => m + (s -> (m.getOrElse(s, 0) + 1))) 
+1

これは 'List'の代わりに' Map'を返しますが、 '@ JanWillemがあなたの答えを受け入れたのを見ても、' update'の代わりとはなりません。 –

+0

これは本当ですが、戻り値の型は異なりますが、到達したい目標に準拠しています。「...各文字列の発生をカウントしたい...」 –

22

ss.groupBy(identity).mapValues(_.size) 
+2

's => s'は' identity'としてあらかじめ定義されています – Landei

+0

'mapValues'? –

+4

このソリューションは非常に頻繁に現れ、実際にはコアライブラリに入るべきです。私はメソッドを「集計」と呼ぶだろう。 –

1

このようなものも動作します:

val l = List("A","B","A","C","A","B") 

l.foldLeft(Map[String,Int]()) { 
    case (a: Map[String, Int], s: String) => { 
    a + (s -> (1 + a.getOrElse(s, 0))) 
    } 
} 

res3: scala.collection.immutable.Map[String,Int] = Map((A,3), (B,2), (C,1)) 
1

貴方の現在のソリューションは、主にListとしてrの選択の、恐ろしく遅いです。しかし、更新の際のリスト全体の反復は、少なくとも改善することができます。私はそれがこの

def update(s: String, l: List[(String, Int)]) : List[(String, Int)] = { 
    l span (_._1 != s) match { 
    case (before, (`s`, count) :: after) => before ::: (s, count + 1) :: after 
    case _ => (s, 1) :: l 
    } 
} 

spanが二度リストを検索する必要がなくなります使用したいと記述します。また、もう一度反復することなく、afterを追加するだけです。

+0

ありがとう!それは貴重なフィードバックです! –

関連する問題