2010-05-21 16 views
9

Y at-il une meilleure façon de le faire:Meilleure façon de marquer et de faire une somme à Scala?

val totalScore = set.foldLeft(0)(_ + score(_)) 

ou ceci:

val totalScore = set.toSeq.map(score(_)).sum 

Je pense qu'il est une opération commune tout à fait aussi attendait plus lisse quelque chose comme:

val totalScore = set.sum(score(_)) 
+1

Les deux premiers morceaux de code font des choses différentes. Voir mon commentaire à la réponse de Daniel C. Sobral. – dsg

+0

Merci @dsg, je l'ai changé. – adam77

Répondre

17

Eh bien, il y a d'autres façons de l'écrire:

val totalScore = set.toSeq.map(score(_)).sum 
val totalScore = set.toSeq.map(score).sum 
val totalScore = set.toSeq map score sum 

Le dernier peut exiger un point-virgule à la fin si la ligne suivante ne démarre pas avec un mot-clé. On peut aussi utiliser .view au lieu de .toSeq, ce qui éviterait d'allouer une collection temporaire. Cependant, je ne suis pas sûr que le comportement actuel de .view (de montrer des éléments répétés) est le bon.

+1

Cela semble être la solution la plus simple et la plus appropriée. Je préférerais lire ceci au lieu d'avoir une fonction qui inclut le mappage. – ziggystar

+3

Une préoccupation légitime avec l'approche en deux étapes peut être la performance - il crée une deuxième liste juste pour l'additionner. Cependant, vous pouvez utiliser une vue pour éviter cette surcharge: 'set.view.map (score) .sum'. Séparer les problèmes de mappage et de sommation évite l'éclatement des méthodes dans la bibliothèque standard. Si vous le faites fréquemment, vous pouvez ajouter 'mapAndSum' à votre propre code. – retronym

+0

Est-ce que quelqu'un a un lien vers de la documentation sur les vues? – adam77

1

Simpler :

scala> val is1 = Set(1, 4, 9, 16) 
is1: scala.collection.immutable.Set[Int] = Set(1, 4, 9, 16) 
scala> is1.reduceLeft(_ + _) 
res0: Int = 30 

Avec votre méthode de score:

scoreSet.reduceLeft(_ + score(_)) 

Attention, cependant, ce n'est la collection étant réduite est vide alors que pli ne:

scala> val is0 = Set[Int]() 
is0: scala.collection.immutable.Set[Int] = Set() 

scala> is0.foldLeft(0)(_ + _) 
res1: Int = 0 
+3

Cela ne fonctionnera pas. Le type de la collection est différent du type du résultat (qui est la raison de l'appel à 'score'), donc' reduceLeft' n'est pas une option. –

1

Alternativement, la surcharge Seq#sum qui prend une conversion implicite Numeric pourrait être utilisé si le type de la collection à marquer/sommer n'a pas lui-même d'opérateur d'addition. Cependant, comme il s'agit d'un paramètre de conversion implicite, il ne sera pas appliqué à moins que cela ne soit requis pour effectuer la vérification de type de réduction de fermeture.

5

Seq.sum ne prend pas une fonction qui pourrait être utilisée pour marquer la somme. Vous pouvez définir une conversion implicite qui "proxénètes" Traversable:

implicit def traversableWithSum[A](t: Traversable[A])(implicit m: Numeric[A]) = new { 
    def sumWith(f: A => A) = t.foldLeft(m.zero)((a, b) => m.plus(a, f(b))) 
} 

def score(i: Int) = i + 1 

val s = Set(1, 2, 3) 

val totalScore = s.sumWith(score _) 
println(totalScore) 
=> 9 

S'il vous plaît noter que le trait Numeric existe que dans Scala 2.8.

+2

Michel, j'aime ta solution et je pensais que je la rendrais un peu plus générique en permettant de marquer sur un objet, comme "Game" et de renvoyer un Numérique: implicite def traversableWithSum [A] (t: Traversable [A]) = new { def sumAvec [B] (f: A => B) (m implicite: Numérique [B]): B = t.froiteGauche (m.zero) ((a, b) => m .plus (a, f (b))) } – Eric