返回嵌套列表的所有组合

时间:2015-06-05 11:37:20

标签: scala

我有以下数据结构

val list = List(1,2,
List(3,4),
List(5,6,7)
)

我希望得到这个结果

List(
List(1,2,3,5), List(1,2,3,6), List(1,2,3,7),
List(1,2,4,5), List(1,2,4,6), List(1,2,4,7)
)

输入中的子列表数量和元素数量可能会有所不同

P.S。

我正在尝试将此作为第一步

list.map{
  case x => List(x)
  case list:List => list
}

和一些理解,但它不起作用,因为我不知道结果的每个子列表将有多少元素

3 个答案:

答案 0 :(得分:3)

在Scala中最常避免使用类似List[Any]的类型 - 语言的大部分功能来自其智能类型系统,而这种类型阻碍了这一点。因此,您将列表转换为规范化List[List[Int]]的直觉就是:

val normalizedList = list.map { 
    case x: Int => List(x)
    case list: List[Int @unchecked] => list 
}

请注意,如果list包含除List以外的某种类型的Int,例如List[String],则由于类型擦除,这最终会引发运行时异常。这正是未能使用强类型时出现的问题!您可以阅读有关处理类型擦除的策略的更多信息here

获得标准化List[List[Int]]后,您可以使用foldLeft来构建组合。你也明白for理解在这里可以很好地运作:

normalizedList.foldLeft(List(List.empty[Int])) { (acc, next) => 
    for { 
        combo <- acc
        num <- next 
    } yield (combo :+ num) 
}

foldLeft的每次迭代中,我们会考虑next中的另一个子列表(normalizedList)。我们查看到目前为止构建的每个组合(combo中的每个acc),然后对于num中的每个数字next,我们通过将其附加到{{ {1}}。

正如您现在所做的那样,combo理解对于formapflatMap操作来说确实是语法糖。所以我们也可以用那些更原始的方法来表达这个:

filter

您甚至可以使用normalizedList.foldLeft(List(List.empty[Int])) { (acc, next) => acc.flatMap { combo => next.map { num => combo :+ num } } } 的{​​{1}}别名,切换地图的顺序,并使用下划线语法以获得最终简洁:

:/

答案 1 :(得分:0)

val list = List(1,2,
  List(3,4),
  List(5,6,7)
)
def getAllCombinations(list: List[Any]) : List[List[Int]] ={
  //normalize head so it is always a List
  val headList: List[Int] = list.head match {
    case i:Int => List(i)
    case l:List[Int] => l
  }
  if(list.tail.nonEmpty){
    // recursion for tail combinations
    val tailCombinations : List[List[Int]] = getAllCombinations(list.tail)

    //combine head combinations with tail combinations
    headList.flatMap(
      {i:Int => tailCombinations.map(
        {l=>List(i).++(l)}
        )
      }
    )
  }
  else{
    headList.map(List(_))
  }
}
print(getAllCombinations(list))

答案 2 :(得分:0)

这也可以通过使用foldLeft来实现。在下面的代码中,通过将每个当前列表与每个新项目组合,将外部列表的每个项目折叠到列表列表中。

val list = List(1,2, List(3,4), List(5,6,7) )

val lxl0 = List( List[Int]() )    //start value for foldLeft
val lxl = list.foldLeft( lxl0 )( (lxl, i) => {
    i match {
        case i:Int          => for( l <- lxl ) yield l :+ i
        case newl:List[Int] => for( l <- lxl; 
                                    i <- newl ) yield l :+ i
    }
})

lxl.map( _.mkString(",") ).foreach( println(_))

虽然我没有使用您想要的地图,但我相信可以更改代码以执行地图并使所有元素列出[Int]。然后,这可以简化foldLeft以简化for-comprehension。但是,我无法立即开始工作;)