2013-04-29 3 views
2

J'ai une méthode load qui est relativement coûteuse à appeler. Afin de permettre une sorte de gestion des exceptions pendant le chargement, il retourne un Try. J'ai maintenant besoin d'une implémentation pour la méthode loadAll qui délègue essentiellement à load pour chacune des clés données. Voici ma première approche, mais je ne sais pas si ce sont les meilleures pratiques en ce qui concerne le travail avec Try. Y a-t-il un meilleur moyen de réaliser ce qui suit?Iterable [Essayez [(K, V)]] pour essayer [Carte [K, V]]

def load(key: K): Try[V] // very expensive 

def loadAll(keys: Traversable[K]): Try[Map[K, V]] = { 

    // remove duplicate keys 
    val ukeys = Set.empty ++ keys 

    val result: Iterable[Try[(K, V)]] = ukeys map { key => 
    load(key) match { 
     case Success(value)  => Success(key, value) 
     case Failure(exception) => Failure(exception) 
    } 
    } 

    Try(result.map { _.get }.toMap) 
} 

Répondre

2

Il est en une ligne à la vanille Scala (en supposant que vous avez déjà Try portée):

def loadAll(keys: Traversable[K]) = Try{ keys.toSet.map((k: K) => (k,load(k).get)).toMap } 
+0

Merci! Il devrait juste être mappé sur un 'Tuple [K, V]' comme '(k, load (k) .get)'. Ai-je raison de dire que cela se répète 3 fois sur les touches (une fois pour 'toSet',' map' et 'toMap')? –

+0

@ManuelSchmidt - J'ai ajusté la réponse pour retourner un tuple. Cela ne fait que répéter trois fois les touches; si «charger» est cher, alors vous ne vous souciez pas de «charger» qui domine tous les autres. –

4

Vous pouvez le faire en utilisant un pli à itérer sur les touches, et pour la compréhension de combiner les Try instances:

def toSet(keys: Traversable[K]): Try[Map[K, V]] = { 
    keys.toSet.foldLeft(Try(Map.empty[K, V])){ case (tryMap, key) => 
    for (m <- tryMap; v <- load(key)) yield m.updated(key, v) 
    } 
} 
+0

Merci !. Fonctionne, mais j'ai dû le modifier légèrement puisque 'distinct' ne semble pas être disponible sur' Traversable' –

+0

Fixé (remplacé par 'distinct' avec' toSet'). –

2

Si vous êtes intéressé par une solution de scalaz, c'est fonctionnement général, disponible via Traversable foncteurs, appelé sequence. Les instances nécessaires pour Try résident dans scalaz-contrib. Voici comment cela pourrait ressembler à:

Welcome to Scala version 2.10.1 (OpenJDK 64-Bit Server VM, Java 1.7.0_21). 
Type in expressions to have them evaluated. 
Type :help for more information. 

scala> import scalaz._, Scalaz._, scalaz.contrib.std.utilTry._ 
import scalaz._ 
import Scalaz._ 
import scalaz.contrib.std.utilTry._ 

scala> import scala.util.Try 
import scala.util.Try 

scala> val result: Iterable[Try[(Int, String)]] = Iterable.empty 
result: Iterable[scala.util.Try[(Int, String)]] = List() 

scala> result.toList.sequence.map(_.toMap) 
res0: scala.util.Try[scala.collection.immutable.Map[Int,String]] = Success(Map()) 

Soit dit en passant, il y a un document "The essence of the Iterator pattern", décrivant/dérivant traverse (et sequence, comme il est le cas particulier). Il y a un bon résumé de cet article par Eric Torreborre ici.

Questions connexes