[英]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))
,而我们的head
是Seq(1,2)
我们知道我们需要一个Seq(Seq(1,3), Seq(1,4), Seq(2,3), Seq(2,4)
作为结果,外部映射(实际上是flatMap
)依次获取每个结果序列,并使用map
在head
序列中的每个元素前加上( +:
:) 。
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.