2013-10-15 134 views
0

我有一个简单的任务来找到最常出现的组合,当我们放下4个立方体的骰子,删除一个最少的点。笛卡尔产品流scala

所以,问题是:是否有任何Scala核心类在Scala中生成笛卡尔产品流?何时不 - 如何以最简单有效的方式实施?

这里是Scala代码和比较幼稚的做法:提前

object D extends App { 
    def dropLowest(a: List[Int]) = { 
    a diff List(a.min) 
    } 

    def cartesian(to: Int, times: Int): Stream[List[Int]] = { 
    def stream(x: List[Int]): Stream[List[Int]] = { 
     if (hasNext(x)) x #:: stream(next(x)) else Stream(x) 
    } 

    def hasNext(x: List[Int]) = x.exists(n => n < to) 

    def next(x: List[Int]) = { 
     def add(current: List[Int]): List[Int] = { 
     if (current.head == to) 1 :: add(current.tail) else current.head + 1 :: current.tail // here is a possible bug when we get maximal value, don't reuse this method 
     } 
     add(x.reverse).reverse 
    } 

    stream(Range(0, times).map(t => 1).toList) 
    } 

    def getResult(list: Stream[List[Int]]) = { 
    list.map(t => dropLowest(t).sum).groupBy(t => t).map(t => (t._1, t._2.size)).toMap 
    } 

    val list1 = cartesian(6, 4) 

    val list = for (i <- Range(1, 7); j <- Range(1,7); k <- Range(1, 7); l <- Range(1, 7)) yield List(i, j, k, l) 
    println(getResult(list1)) 
    println(getResult(list.toStream) equals getResult(list1)) 
} 

感谢

+0

如何http://stackoverflow.com/questions/8321906/lazy-cartesian-product-几个seqs-in-scala?不流,但仍然懒惰 –

回答

0

我想你可以通过使用flatMap简化代码:

val stream = (1 to 6).toStream 

def cartesian(times: Int): Stream[Seq[Int]] = { 
    if (times == 0) { 
    Stream(Seq()) 
    } else { 
    stream.flatMap { i => cartesian(times - 1).map(i +: _) } 
    } 
} 

也许更高效一点(记忆方式)将会使用迭代器代替:

val pool = (1 to 6) 

def cartesian(times: Int): Iterator[Seq[Int]] = { 
    if (times == 0) { 
    Iterator(Seq()) 
    } else { 
    pool.iterator.flatMap { i => cartesian(times - 1).map(i +: _) } 
    } 
} 

甚至更​​简洁,通过折叠替换递归调用:

def cartesian[A](list: Seq[Seq[A]]): Iterator[Seq[A]] = 
    list.foldLeft(Iterator(Seq[A]())) { 
     case (acc, l) => acc.flatMap(i => l.map(_ +: i)) 
    } 

然后:

cartesian(Seq.fill(4)(1 to 6)).map(dropLowest).toSeq.groupBy(i => i.sorted).mapValues(_.size).toSeq.sortBy(_._2).foreach(println) 

(请注意,您不能在迭代器使用GROUPBY,所以流甚至列表是去做什么的方式;上面的代码仍然有效,因为toSeq在一个Iterator实际上返回一个懒惰的Stream)。

如果你正在考虑骰子,而不是组合的总和统计,你可以更新dropLowest fonction: def dropLowest(l: Seq[Int]) = l.sum - l.min