Scala - can yield be used multiple times with a for loop?

◇◆丶佛笑我妖孽 提交于 2019-12-17 19:37:31

问题


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.


回答1:


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)



回答2:


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.




回答3:


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.




回答4:


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))



回答5:


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)



回答6:


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



来源:https://stackoverflow.com/questions/1140164/scala-can-yield-be-used-multiple-times-with-a-for-loop

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!