Question

An example:

val l = List(1,2,3)
val t = List(-1,-2,-3)

Can I do something like this?

for (i <- 0 to 10) yield (l(i)) yield (t(i))

Basically I want to yield multiple results for every iteration.

Was it helpful?

Solution

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

So here's one possibility that you might be 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 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 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 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 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)

OTHER TIPS

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

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

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

Yields can be nested, which would result ...

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 solution is semantically different.

Here is a type-agnostic solution for an unknown, varying number of elements in a 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 overengineered. You could although call it

   xproduct (List (l, t))

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

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

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

For example:

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

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top