This might be a little unhelpful, but why are you trying to do it that way? I ran the code and tried to understand what it was doing, but found that I needed to convert it into an imperative style to comprehend it. Something like:
var pairsAcrossPartitions = [(Int, Int, B, B)]()
for (i, partition1) in partitions.enumerated() {
for (j, partition2) in partitions.enumerated() {
for el1 in partition1 {
for el2 in partition2 {
pairsAcrossPartitions.append( (i, j, el1, el2) )
}
}
}
}
is much easier to understand to me, and will also be faster if the compiler can't figure out what you're trying to do (e.g. in a debug build, where all those intermediate arrays won't be optimised out).
A couple other small things about your code snippet: firstly, a note that the Array() constructors around the return values of flatMap are unnecessary. Secondly, you don't have to create the p1 and p2 temporaries; you can simply do this:
let pairsAcrossPartitions = partitions.enumerated().flatMap { (i, partition1) -> [(Int, Int, B, B)] in
return partitions.enumerated().flatMap { (j, partition2) -> [(Int, Int, B, B)] in
return partition1.flatMap { (el1: B) -> [(Int, Int, B, B)] in
return partition2.map { (el2: B) -> (Int, Int, B, B) in
return (i, j, el1, el2)
}
}
}
}
Now to your actual question. If you're tied to a functional style, that inner loop to me looks like the result of zipping partition1 and partition2; you could go something like:
zip(partition1.lazy.map { repeatElement($0, count: partition2.count) }, repeatCollection(partition2) }.map { (i, j, $0, $1) }
using a hypothetical repeatCollection primitive (an example can be seen here) that infinitely loops through the source sequence. You could apply a similar approach to the outer sequence. Again, though, I personally think an imperative style is the way to go with this particular problem.