[ACCEPTED]-Scala - can yield be used multiple times with a for loop?-cartesian-product

Accepted answer
Score: 21

It's not clear what you're asking for - what 10 you expect the semantics of multiple yield 9 to be. One thing, though, is that you probably 8 never want to use indexes to navigate a 7 list - each call to t(i) is O(i) to execute.

So 6 here's one possibility that you might be 5 asking for

scala> val l = List(1,2,3); val t = List(-1,-2,-3)
l: List[Int] = List(1, 2, 3)
t: List[Int] = List(-1, -2, -3)

scala> val pairs = l zip t
pairs: List[(Int, Int)] = List((1,-1), (2,-2), (3,-3))

And here's another possibility 4 that you might be asking for

scala> val crossProduct = for (x <- l; y <- t) yield (x,y)
crossProduct: List[(Int, Int)] = List((1,-1), (1,-2), (1,-3), (2,-1), (2,-2), (2,-3), (3,-1), (3,-2), (3,-3))

The later is 3 just syntactic sugar for

scala> val crossProduct2 = l flatMap {x => t map {y => (x,y)}}
crossProduct2: List[(Int, Int)] = List((1,-1), (1,-2), (1,-3), (2,-1), (2,-2), (2,-3), (3,-1), (3,-2), (3,-3))

A third possibility 2 is you want to interleave them

scala> val interleaved = for ((x,y) <- l zip t; r <- List(x,y)) yield r
interleaved: List[Int] = List(1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6, 7, -7, 8, -8, 9, -9, 10, -10)

That's syntax 1 sugar for

scala> val interleaved2 = l zip t flatMap {case (x,y) => List(x,y)}
interleaved2: List[Int] = List(1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6, 7, -7, 8, -8, 9, -9, 10, -10)
Score: 6

No, you can't use multiple yield clauses, but 5 there are work-arounds. For example:

for (i <- 0 to 10;
     r <- List(l(i), t(i)))
yield r

You 4 can nest for-comprehensions, of course, but 3 that would result in a list of lists of 2 elements, which I don't believe is what 1 you want.

Score: 2

Yields can be nested, which would result 2 ...

for (i <- 0 to 3) yield {
  for (j <- 0 to 2) yield (i,j)
}

in a Vector of Vector:

scala.collection.immutable.IndexedSeq[scala.collection.immutable.IndexedSeq[(Int, Int)]]
= Vector(Vector((0,0), (0,1), (0,2)), Vector((1,0), (1,1), (1,2)), Vector((2,0), (2,1), (2,2)), Vector((3,0), (3,1), (3,2)))

for (i <- 0 to 3;
  j <- 0 to 2) yield (i,j)

The flattened 1 solution is semantically different.

Score: 1

Here is a type-agnostic solution for an 4 unknown, varying number of elements in a 3 unknown number of lists:

def xproduct (xx: List [List[_]]) : List [List[_]] = 
  xx match {
    case aa :: bb :: Nil => 
      aa.map (a => bb.map (b => List (a, b))).flatten       
    case aa :: bb :: cc => 
      xproduct (bb :: cc).map (li => aa.map (a => a :: li)).flatten
    case _ => xx
}

For 2 Lists it is 2 overengineered. You could although call 1 it

   xproduct (List (l, t))
Score: 0

Apparently not. I get a compile error when 5 I try it.

It looks like for .. yield is 4 an expression. You can't have two yields, since 3 that's not really part of the expression.

If 2 you want to yield multiple values, why not 1 yield them as a tuple or a list?

For example:

for( t <- List(1,2,3); l <- List(-1,-2,-3))
  yield (t, l)
Score: 0

Maybe yield is not the best way to go? Perhaps 1 simple array appending could be used here.

More Related questions