2011-10-10 33 views
7

我在想如何在Scala中編寫一個採用函數f和參數列表args的方法,其中每個參數都是一個範圍。假設我有三個參數(Range(0,2),Range(0,10)Range(1, 5))。然後我想用這三個參數的所有可能性遍歷f使用可變參數驗證概率分佈總和爲1

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

但是,我希望此方法可用於具有可變數量參數的函數。這可能嗎?

編輯:有沒有辦法做到這一點,當功能沒有列表,而是採取標準參數列表或咖喱?

+1

我認爲這可以通過遞歸,但只有在'f'接受值列表。 –

+0

我希望在沒有這個限制的情況下解決這個問題('f'需要一個標準參數列表或curried)。 – schmmd

回答

6

這真是個好問題!

您想在任意大小的元素列表上依次運行flatMap。當你不知道你的列表有多長時間時,你可以用遞歸處理它,或者等價地用摺疊處理它。

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

(如果你不能找出代碼,不用擔心,學習如何使用Hooglesteal it from Haskell

你可以用Scalaz做到這一點(在總體上與F[G[X]]並開始返回G[F[X]],鑑於類型構造GF分別有TraverseApplicative能力。

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

很酷的答案 - 我一定要檢查scalaz。 – schmmd

1

這將或多或少地做工作(不將f應用,你可以單獨做)

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

然後,您可以只是地圖上的該功能。我不確定這是一個非常有效的實現。

0

這是遞歸角度的答案。不幸的是,並不像其他人那麼短。

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

看看this answer。我使用這個代碼來達到這個目的。它略有優化。如果你需要的話,我想我可以製作更快的版本。