Могу ли я посчитать вхождение в 1 цикл в этом фрагменте Scala? - PullRequest
1 голос
/ 25 ноября 2010

У меня есть простой фрагмент кода Scala.Я последовательно перебираю список строк и хочу подсчитать вхождение каждой строки, которую я собираю в виде кортежей (String, Int) в списке r.Часть в основной функции должна остаться (поэтому нет groupBy или что-то в этом роде).Мой вопрос о функции обновления:

, сейчас я сначала делаю find, а затем добавляю новый кортеж к r, если он не существует.Если он существует, я перебираю r и обновляю счетчик для соответствующей строки.

Можно ли изменить функцию обновления, чтобы она была более эффективной?Можно ли обновить r за одну итерацию (добавление, если оно не существует, обновление счетчика, если оно существует)?

Спасибо

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
}

Ответы [ 4 ]

22 голосов
/ 25 ноября 2010

Я предлагаю вам перейти на функциональный стиль и использовать всю мощь коллекций Scala:

ss.groupBy(identity).mapValues(_.size)
4 голосов
/ 25 ноября 2010

Если вы не хотите использовать groupBy или работать с отложенными коллекциями (Streams), это может быть путь:

ss.foldLeft(Map[String, Int]())((m, s) => m + (s -> (m.getOrElse(s, 0) + 1)))
1 голос
/ 25 ноября 2010

Ваше настоящее решение ужасно медленное, в основном из-за выбора r в качестве List.Но ваша итерация по всему списку в случае обновления может быть улучшена, по крайней мере.Я бы написал так:

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, не повторяя его снова.

1 голос
/ 25 ноября 2010

Примерно так же работает:

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))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...