2010-05-23 8 views

Respuesta

17

Creo que descubrí cómo hacerlo.

def combine(acc:Set[String], set:Set[String]) = for (a <- acc; s <- set) yield { 
    a + "&" + s 
} 

val expanded = sets.reduceLeft(combine) 

expanded: scala.collection.immutable.Set[java.lang.String] = Set(b&2&T, a&1&S, 
    a&1&T, b&1&S, b&1&T, c&1&T, a&2&T, c&1&S, c&2&T, a&2&S, c&2&S, b&2&S) 
+0

Sincronicidad! En mi solución, la construcción diferida de las cuerdas hasta el último paso, pero son básicamente las mismas. – retronym

12

Buena pregunta. Aquí está una manera:

scala> val seqs = Seq(Seq("a","b","c"), Seq("1","2"), Seq("S","T"))     
seqs: Seq[Seq[java.lang.String]] = List(List(a, b, c), List(1, 2), List(S, T)) 

scala> val seqs2 = seqs.map(_.map(Seq(_))) 
seqs2: Seq[Seq[Seq[java.lang.String]]] = List(List(List(a), List(b), List(c)), List(List(1), List(2)), List(List(S), List(T))) 

scala> val combined = seqs2.reduceLeft((xs, ys) => for {x <- xs; y <- ys} yield x ++ y) 
combined: Seq[Seq[java.lang.String]] = List(List(a, 1, S), List(a, 1, T), List(a, 2, S), List(a, 2, T), List(b, 1, S), List(b, 1, T), List(b, 2, S), List(b, 2, T), List(c, 1, S), List(c, 1, T), List(c, 2, S), List(c, 2, T)) 

scala> combined.map(_.mkString("&"))    
res11: Seq[String] = List(a&1&S, a&1&T, a&2&S, a&2&T, b&1&S, b&1&T, b&2&S, b&2&T, c&1&S, c&1&T, c&2&S, c&2&T) 
+0

Gracias. Primero probé con foldLeft y eventualmente descubrí que necesitaba usar reductionLeft en su lugar (seguía obteniendo un resultado vacío). ¿La conversión a Seq solo es necesaria para preservar el orden? – huynhjl

+0

Combinar al final en realidad será útil porque los conjuntos están de hecho en el mismo espacio y necesito combinar "b & a & a" en "a & b" (eliminar dups y ordenar la combinación). – huynhjl

+0

@huynhjl El uso de seq (para empezar) probablemente era para que el retrónimo pudiera evitar la importación de 'scala.collection.immutable.Set', y tal vez para mostrar que esto podría hacerse con la interfaz más general. –

6

se produjo después de la Batle;) pero otro:

sets.reduceLeft((s0,s1)=>s0.flatMap(a=>s1.map(a+"&"+_))) 
3

Ampliando @Patrick's answer. Ahora es más general y más perezoso:

def combine[A](f:(A, A) => A)(xs:Iterable[Iterable[A]]) = 
    xs.reduceLeft { (x, y) => x.view.flatMap {a => y.map(f(a, _)) } } 

tener que ser perezoso le permite ahorrar espacio, ya que no almacena los exponencialmente muchos elementos en el conjunto ampliado; en cambio, los generas sobre la marcha. Pero, si realmente se desea que el conjunto completo, todavía puede conseguir de esta manera:

val expanded = combine{(x:String, y:String) => x + "&" + y}(sets).toSet 
+0

Aquí hay una solución más general que permite una aplicación del mapa antes de tomar el producto cartesiano: http://stackoverflow.com/a/4515050/244526 – dsg

3

Ampliando dsg's answer, se puede escribir con más claridad (creo) de esta manera, si no te importa el curry función:

def combine[A](f: A => A => A)(xs:Iterable[Iterable[A]]) = 
    xs reduceLeft { (x, y) => x.view flatMap { y map f(_) } } 

Otra alternativa (un poco más largo, pero mucho más fácil de leer):

def combine[A](f: (A, A) => A)(xs:Iterable[Iterable[A]]) = 
    xs reduceLeft { (x, y) => for (a <- x.view; b <- y) yield f(a, b) } 

Uso:

combine[String](a => b => a + "&" + b)(sets) // curried version 

combine[String](_ + "&" + _)(sets)    // uncurried version 
+0

¡increíble, gracias! – dsg

Cuestiones relacionadas