Tengo un método en un programa scala que está creando un Mapa [Tuple2 [String, String], Int] pero se ejecuta muy lentamente y no puede procesar mucho texto. Parece que no puedo entender cómo acelerarlo y hacerlo más eficiente. Cualquier sugerencia sería muy apreciada.

def createTuple(words: List[String]): Map[Tuple2[String, String], Int] = {
    var pairCountsImmutable = Map[Tuple2[String, String], Int]()
    val pairCounts = collection.mutable.Map(pairCountsImmutable.toSeq: _*)
    var i = 0
    for (i <- 0 to words.length - 2) {
        val currentCount: Int = pairCounts.getOrElse((words(i), words(i + 1)), 0)
        if (pairCounts.exists(_ == (words(i), words(i + 1)) -> currentCount)) {
            var key = pairCounts(words(i), words(i + 1))
            key = key + 1
            pairCounts((words(i), words(i + 1))) = key
        } else {
            pairCounts += (words(i), words(i + 1)) -> 1
        }
    }
    var pairCountsImmutable2 = collection.immutable.Map(pairCounts.toList: _*)
    return pairCountsImmutable2
}
1
ejscribner 8 sep. 2018 a las 20:08

3 respuestas

La mejor respuesta

Actualizar

TRuhland me prestó descaradamente la respuesta para dar esta versión mejorada de mi respuesta que no falla con las listas vacías o de un solo elemento:

def createTuple(words: List[String]): Map[Tuple2[String, String], Int] =
  words
    .zip(words.drop(1))
    .groupBy(identity)
    .mapValues(_.length)

Original

Parece que estás contando pares de palabras adyacentes es una lista de palabras. Si es así, algo como esto debería funcionar:

def createTuple(words: List[String]): Map[Tuple2[String, String], Int] =
  words
    .sliding(2)
    .map(l => (l(0), l(1)))
    .toList
    .groupBy(identity)
    .mapValues(_.length)

Esto funciona de la siguiente manera

  1. sliding(2) crea una lista de pares de palabras adyacentes
  2. map convierte cada par de un List en una tupla
  3. groupBy agrupa las tuplas con el mismo valor
  4. mapValues cuenta el número de pares con el mismo valor para cada par

Puede que esto no sea exactamente lo que desea, pero con suerte le dará una idea de cómo podría hacerse.

Como regla general, no recorra una lista utilizando un índice, sino intente transformar la lista en algo donde pueda recorrer los valores.

Intenta no crear Map s elemento por elemento. Utilice groupBy o toMap.

2
Tim 9 sep. 2018 a las 07:19

Si primero reducimos su código a la esencia:

def createTuple(words: List[String]): Map[(String, String), Int] = {
    val pairCounts = collection.mutable.Map[(String, String), Int]()
    for (i <- 0 until words.length - 1) {
      val pair = (words(i), words(i + 1))
      pairCounts += (pair  -> (pairCounts.getOrElse(pair, 0) + 1))
    }
    pairCounts.toMap
  }

Para mejorar la velocidad, no use la indexación en la lista (como se menciona en otra parte):

def createTuple(words: List[String]): Map[(String, String), Int] = {
  val map = collection.mutable.Map[(String, String), Int]()
  words
    .zip(words.tail)
    .foreach{ pair => 
       map += (pair -> (map.getOrElse((pair, 0) + 1)) }
  map.toMap
}
1
TRuhland 9 sep. 2018 a las 00:32

Su gran problema es que words es un List y, sin embargo, lo está indexando con words(i). Eso es lento Cámbielo para que sea un Vector o modifique su algoritmo para no usar la indexación.

Además, pairCounts.exists es lento, debe usar contains siempre que sea posible, ya que es un tiempo constante en un Mapa.

1
Brian McCutchon 8 sep. 2018 a las 17:32