对Scala中动态深度的理解

时间:2014-07-24 14:03:19

标签: scala functional-programming

我正在编写一个代码,需要生成整数序列的所有组合,这些整数序列在两个其他整数序列的边界内(元素方面)。代码可能比上面的解释更具可读性:

def combinations(startingCounts: List[Int], endingCounts: List[Int] ) = for(
  a <- startingCounts(0) to endingCounts(0);
  b <- startingCounts(1) to endingCounts(1);
  c <- startingCounts(2) to endingCounts(2)
) yield List(a, b, c)

combinations(List(0,7,3), List(1,7,5)) 
//^returns Vector(List(0, 7, 3), List(0, 7, 4), List(0, 7, 5), List(1, 7, 3), List(1, 7, 4), List(1, 7, 5))

上面的代码按预期工作,但它有两个问题:

  • 它只适用于一定长度的列表。这对我的用例来说并不是一个问题,但总的来说是。
  • 代码长度与我需要处理的列表长度成比例。在我的情况下,长度是6,我有6个发电机的理解。

我的问题是:实现相同功能的最佳方式是什么,它适用于所有&#34;绑定列表&#34; lenghts?通过&#34; best&#34;我的意思是正确的,simple enough,最好不要比原来慢得多。

2 个答案:

答案 0 :(得分:2)

这个怎么样?

def combinations(startingCounts: List[Int], endingCounts: List[Int] ) : IndexedSeq[List[Int]] = {
  if(startingCounts.isEmpty)
    IndexedSeq(Nil)
  else 
    for{
      ns <- combinations(startingCounts.tail, endingCounts.tail)
      n <- startingCounts.head to endingCounts.head
    } yield 
      n :: ns
}

答案 1 :(得分:1)

这是我最初的解决方案。它看起来不错,但我想知道它是否可以做得更好。

import scala.annotation.tailrec

type SLInt = IndexedSeq[List[Int]]
def combinations2(startingCounts: List[Int], endingCounts: List[Int] ): SLInt = {
  @tailrec
  def inner(acc: SLInt, startingCounts: List[Int], endingCounts: List[Int]): SLInt = {
    (startingCounts, endingCounts) match {
      case (sh :: st, eh :: et) if (sh <= eh) => {
        val newAcc = for(
          ls <- acc;
          last <- (sh to eh)
        ) yield (last :: ls)
        inner(newAcc, st, et)
      }
      case (Nil, Nil) => acc
      case _ => throw new IllegalArgumentException()
    }
  }
  inner(IndexedSeq(List()), startingCounts.reverse, endingCounts.reverse)
}

combinations2(List(0,7,3), List(1,7,5))
//res3: SLInt = Vector(List(0, 7, 3), List(1, 7, 3), List(0, 7, 4), List(1, 7, 4), List(0, 7, 5), List(1, 7, 5))

结果的顺序不同,但这没有什么区别。我正在执行List.reverse以避免使用List追加操作并改为使用前置,这应该是常量时间。