2011-10-10 13 views
7

Mi chiedevo come scrivere un metodo in Scala che accetta una funzione f e un elenco di argomenti args in cui ogni argomento è un intervallo. Supponiamo di avere tre argomenti (Range(0,2), Range(0,10) e Range(1, 5)). Quindi voglio ripetere su f con tutte le possibilità di questi tre argomenti.verifica una distribuzione di probabilità con argomenti variabili somme a 1

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

Tuttavia, voglio che questo metodo funzioni per funzioni con un numero variabile di argomenti. È possibile?

Modifica: c'è un modo per farlo quando la funzione non accetta una lista, ma prende piuttosto un elenco di parametri standard o è in fase di elaborazione?

+1

Penso che sia possibile tramite la ricorsione, ma solo se 'f' accetta un elenco di valori. –

+0

Speravo di risolverlo senza questa limitazione ('f' prende una lista di parametri standard o è al curry). – schmmd

risposta

6

Questa è davvero una bella domanda!

Si desidera eseguire flatMap in sequenza su un elenco di elementi di dimensione arbitraria. Quando non sai quanto è lunga la tua lista, puoi elaborarla con ricorsione, o equivalentemente, con una piega.

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)) 

(Se non si riesce a capire il codice, non ti preoccupare, imparare ad usare Hoogle e steal it from Haskell)

È possibile farlo con Scalaz (in genere si inizia con un F[G[X]] e restituisce un G[F[X]], visto che i costruttori di tipo G e F hanno le capacità e TraverseApplicative rispettivamente.

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

Cool answer - Devo senz'altro controllare lo scalaz. – schmmd

1

Che sarebbe più o meno fare il lavoro (senza applicare f, che si può fare a parte)

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

È possibile quindi solo mappare la funzione su questo. Non sono sicuro che sia un'implementazione molto efficiente.

0

Questa è la risposta dalla prospettiva ricorsiva. Sfortunatamente, non così breve come gli altri.

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

Dai un'occhiata allo this answer. Io uso questo codice esattamente per questo scopo. È leggermente ottimizzato. Penso che potrei produrre una versione più veloce se ne hai bisogno.

Problemi correlati