2011-10-10 11 views
7

Me preguntaba cómo escribirías un método en Scala que toma una función f y una lista de argumentos args donde cada arg es un rango. Supongamos que tengo tres argumentos (Range(0,2), Range(0,10) y Range(1, 5)). Entonces quiero iterar sobre f con todas las posibilidades de esos tres argumentos.verificando una distribución de probabilidad con sumas variables de argumentos a 1

var sum = 0.0 
for (a <- arg(0)) { 
    for (b <- arg(1)) { 
    for (c <- arg(2)) { 
     sum += f(a, b, c) 
    } 
    } 
} 

Sin embargo, deseo que este método funcione para funciones con un número variable de argumentos. es posible?

Editar: ¿hay alguna manera de hacer esto cuando la función no toma una lista, sino que toma una lista de parámetros estándar o está curried?

+1

Creo que es posible a través de la recursión, pero solo si 'f' acepta la lista de valores. –

+0

Esperaba resolverlo sin esta restricción ('f' toma una lista de parámetros estándar o está al curry). – schmmd

Respuesta

6

¡Esa es una muy buena pregunta!

Quiere ejecutar flatMap en secuencia sobre una lista de elementos de tamaño arbitrario. Cuando no sabe cuánto tiempo es su lista, puede procesarla con recursividad, o de manera equivalente, con un doblez.

scala> def sequence[A](lss: List[List[A]]) = lss.foldRight(List(List[A]())) { 
    | (m, n) => for (x <- m; xs <- n) yield x :: xs 
    | } 
scala> sequence(List(List(1, 2), List(4, 5), List(7))) 
res2: List[List[Int]] = List(List(1, 4, 7), List(1, 5, 7), List(2, 4, 7), List(2 
, 5, 7)) 

(Si no puede averiguar el código, no se preocupe, aprender a utilizar Hoogle y steal it from Haskell)

Usted puede hacer esto con Scalaz (en general se comienza con una F[G[X]] y devuelve un G[F[X]], dado que los constructores de tipo G y F tienen las capacidades Traverse y Applicative respectivamente.

scala> import scalaz._ 
import scalaz._ 

scala> import Scalaz._ 
import Scalaz._ 

scala> List(List(1, 2), List(4, 5), List(7)).sequence 
res3: List[List[Int]] = List(List(1, 4, 7), List(1, 5, 7), List(2, 4, 7), List(2 
, 5, 7)) 

scala> Seq(some(1), some(2)).sequence 
res4: Option[Seq[Int]] = Some(List(1, 2)) 

scala> Seq(some(1), none[Int]).sequence 
res5: Option[Seq[Int]] = None 
+0

Respuesta interesante: Definitivamente tendré que revisar el scalaz. – schmmd

1

Eso sería más o menos hacer el trabajo (sin aplicar f, lo que puede hacer por separado)

def crossProduct[A](xxs: Seq[A]*) : Seq[Seq[A]] 
    = xxs.foldLeft(Vector(Vector[A]())){(res, xs) => 
     for(r <- res; x <- xs) yield r :+ x 
    } 

continuación, puede simplemente asignar su función en eso. Sin embargo, no estoy seguro de que sea una implementación muy eficiente.

0

Esa es la respuesta desde la perspectiva recursiva. Desafortunadamente, no tan corto como otros.

def foo(f: List[Int] => Int, args: Range*) = { 
    var sum = 0.0 
    def rec(ranges: List[Range], ints: List[Int]): Unit = { 
     if (ranges.length > 0) 
     for (i <- ranges.head) 
      rec(ranges.tail, i :: ints) 
     else 
     sum += f(ints) 
    } 
    rec(args.toList, List[Int]()) 
    sum 
    } 
0

Eche un vistazo a this answer. Uso este código para exactamente este propósito. Está ligeramente optimizado. Creo que podría producir una versión más rápida si la necesitas.

Cuestiones relacionadas