简体   繁体   English

Scala-折叠多个Seq

[英]Scala - collapse multiple Seqs

How can I transform a Seq[Seq[Int] such that each inner Seq contains the elements from the other two lists? 如何转换Seq [Seq [Int],使每个内部Seq包含其他两个列表中的元素?

//Example:
val A = Seq(1, 2, 3)
val B = Seq(4, 5)
val C = Seq(6, 7, 8)
val input(A,B,C)
/**
  * INPUT: [(1,2,3), (4,5), (6,7,8)]
  * OUTPUT:[(1,4,6), (1,4,7), (1,4,8), (1,5,6), ..., (3,5,8)]
  */
def process(input: Seq[Seq[Int]]): Seq[Seq[Int]] = {
    //unknown
}

If this is a homework assignment, I recommend you don't turn in the following (you might to reverse engineer a simpler solution involving recursion out of it I guess). 如果这是一项家庭作业,则建议您不要上交以下内容(我想您可能会反向设计一个涉及递归的简单解决方案)。 However, if you are really just curious about this, there are some pretty neat tricks. 但是,如果您真的对此感到好奇,可以使用一些漂亮的技巧。

def convert[T](xs: Seq[Seq[T]]): Seq[Seq[T]] = 
  xs.foldLeft(Seq(Seq[T]()))((bs,ns) => for (b <- bs; n <- ns) yield b :+ n)

foldLeft emphasizes the fact that you will be starting from the left side of the input, working your way right one Seq at a time. foldLeft强调了一个事实,您将从输入的左侧开始,一次向右移动一个Seq Then, for every Seq you find, you take a cartesian product of sorts. 然后,对于找到的每个Seq ,您都会采用各种笛卡尔乘积。

scala> convert(Seq(Seq(1,2,3), Seq(4,5), Seq(6,7,8)))
res: Seq[Seq[Int]] = List(List(1, 4, 6), List(1, 4, 7), List(1, 4, 8), 
List(1, 5, 6), List(1, 5, 7), List(1, 5, 8), List(2, 4, 6), List(2, 4, 7), 
List(2, 4, 8), List(2, 5, 6), List(2, 5, 7), List(2, 5, 8), List(3, 4, 6), 
List(3, 4, 7), List(3, 4, 8), List(3, 5, 6), List(3, 5, 7), List(3, 5, 8))

This is a standard recursive notion of manipulating a list of lists (or Sequences in your case). 这是处理列表列表(或您的情况下的序列)的标准递归概念。 The key is to invert your thinking and consider how you 'build' each candidate case. 关键是要转变思维,并考虑如何“构建”每个候选案例。 Note that, as the other solution shows, there are several ways of solving this, all of which more or less reduce into each other. 注意,如另一个解决方案所示,有几种解决方法,所有这些方法或多或少地相互简化。

The solution I show here is correct except for ordering (intentionally). 我在这里显示的解决方案是正确的,但有意订购除外。 It should show the necessary concepts, though: 但是,它应该显示必要的概念:

def process(input: Seq[Seq[Int]]): Seq[Seq[Int]] = input match {
  case Nil => Seq()
  case head :: Nil => head.map((element: Int) => Seq(element))
  case (head: Seq[Int]) :: tail => process(tail).flatMap(
    (tailSeq: Seq[Int]) => {
      head.map((element: Int) => element +: tailSeq)
    })

}

// In the worksheet
val processResult = process(input)  // Visual inspection shows correctness
processResult.length == (A.length * B.length * C.length)  // Just verify we get the correct number of values

When considering recursion the first thing you should do is consider your base case . 在考虑递归时,您应该做的第一件事就是考虑基本情况 In this case, what happens if the input list is empty? 在这种情况下,如果input列表为空会怎样? We know this to be an empty Seq . 我们知道这是一个空的Seq This is useful because we can build our solution on top of it. 这很有用,因为我们可以在此基础上构建解决方案。 Now, I cheat a little and suggest a second base case (any case that doesn't recurse is a base case) where we have only a single sequence: we know that the result of this is a sequence of sequences, where each subsequence has a single element. 现在,我作弊一下,并建议第二个基本情况(任何不递归的情况都是一个基本情况),其中我们只有一个序列:我们知道它的结果是一个序列序列,其中每个子序列都有一个元素。

We use map to construct this, where we convert or 'map' each element in the input sequence ( head ) to a single-element sequence. 我们使用map来构造它,在这里我们将输入序列( head )中的每个元素转换或“映射”为单元素序列。 All of these together is the base sequence we build the rest of our solution on. 所有这些共同构成了我们其余解决方案的基础序列。

Given that foundation, we consider what happens if there is an unknown number of additional sequences in our input (the tail ) and some new sequence (the head ) - this is called the general case . 在此基础上,我们考虑如果input的附加序列( tail )和一些新序列( head )中未知数量的附加序列会发生什么,这称为一般情况 We can trust that process will return a correct solution for all the rest of those sequences, so we have it do that with process(tail) . 我们可以相信,该process将为所有其余序列返回正确的解决方案,因此我们可以使用process(tail)进行process(tail) Now we just need to take that and transform it using head . 现在我们只需要接受它并使用head对其进行转换。

Again, we want to map across all of the resultant sequence. 同样,我们要map所有结果序列。 If processing the tail returned Seq(Seq(3), Seq(4)) , and our head is Seq(1,2) we know we need a Seq(Seq(1,3), Seq(1,4), Seq(2,3), Seq(2,4) as our result. The outer map (actually flatMap ) takes each of the resultant sequence in turn and uses map to prepend ( +: ) each element in the head sequence. 如果处理tail返回Seq(Seq(3), Seq(4)) ,而我们的headSeq(1,2)我们知道我们需要一个Seq(Seq(1,3), Seq(1,4), Seq(2,3), Seq(2,4)作为结果,外部映射(实际上是flatMap )依次获取每个结果序列,并使用maphead序列中的每个元素前加上( +: :)

If we were to use map instead of flatMap the result would be: Seq(Seq(Seq(1,3), Seq(1,4)), Seq(Seq(2,3), Seq(2,4))) . 如果我们使用map而不是flatMap则结果将是: Seq(Seq(Seq(1,3), Seq(1,4)), Seq(Seq(2,3), Seq(2,4))) What flatMap does is simply 'flatten' the head mapping so the results of apply each head element all end up in the same sequence. flatMap所做的只是简单地“平整” head映射,因此应用每个head元素的结果全部以相同的顺序结束。 (Exercise for the reader: what happens if the second map is replaced with flatMap ?) (供读者练习:如果将第二张map替换为flatMap怎样?)

So, given a correct processing of tail we know we can add an arbitrary sequence and get a correct output. 因此,给定正确的tail处理,我们知道可以添加任意序列并获得正确的输出。 This is all that's needed, and the magic of recursion! 这就是全部所需要的,还有递归的魔力!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM