2012-05-17 1 views
1

Je suis en train de créer une bibliothèque de vecteurs sparse fly-by-seat-of-pants dans scala et je rencontre un problème avec foldLeft où il semble créer ou ajouter un élément supplémentaire sur les séquences de longueur 1.Scala foldLeft ajoutant un élément de plus?

Voilà ma fonction plus clairsemée:

def addTwoMaps(m1: Map[Int,Double], m2: Map[Int,Double]) = 
    m1 ++ m2.map{ case (k,v) => k -> (v + m2.getOrElse(k, 0.)) } 

Et voici mon « ajouter sur la séquence de cartes/vecteurs rares et normaliser » la fonction:

def addNMaps(ms : Map[Int, Double]*) = { 
val denom = if (ms.length > 0) ms.length.toDouble else 1 
ms.foldLeft(Map.empty[Int, Double])((a,b) => addTwoMaps(a,b)).mapValues(_/denom) 
} 

(pour mon cas particulier, les valeurs chaque somme d'entrée somme à un donc tout ce que je dois faire est diviser par la longueur de la séquence d'arguments pour s'assurer que la carte résultante somme à un sur ses valeurs)

Comme test c ase, si j'ajoute deux cartes qui ont des valeurs qui résument à un, il fonctionne bien:

scala> Common.addNMaps(Map(1->1), Map(1->1)) 
res34: scala.collection.immutable.Map[Int,Double] = Map(1 -> 1.0) 

Mais si j'ai qu'un seul argument:

scala> Common.addNMaps(Map(1->1)) 
res33: scala.collection.immutable.Map[Int,Double] = Map(1 -> 2.0) 

La somme des valeurs à deux tous un soudain! Ma conjecture est que le simple Map(1->1) est ajouté deux fois en quelque sorte dans foldLeft mais c'est seulement une supposition.

Qu'est-ce que je fais mal? Et comment puis-je obtenir Common.addNMaps(Map(1->1)) pour retourner Map(1->1.0)?

Répondre

1

Il y a une faute de frappe dans votre addTwoMaps, qui devrait être:

def addTwoMaps(m1: Map[Int,Double], m2: Map[Int,Double]) = 
    m1 ++ m2.map{ case (k,v) => k -> (v + m1.getOrElse(k, 0.)) } 

Vous devez appeler getOrElse sur m1, non m2.


Notez que dans ce cas, vous pouvez utiliser un IntMap, qui a une méthode pratique unionWith (probablement inspiré par Haskell's Data.Map.unionWith):

import scala.collection.immutable.IntMap 

def addNMaps(ms : IntMap[Double]*) = { 
    val denom = if (ms.length > 0) ms.length else 1 
    ms.foldLeft(IntMap.empty[Double]) { 
    (a, b) => a.unionWith(b, (_, x, y) => x + y) 
    }.mapValues(_/denom) 
} 

Je ne sais pas pourquoi unionWith ne fait pas partie de l'API standard Scala Map.

+0

D'oh! Yup, ça a tout arrangé. Merci! – JasonMond