2

I want to write a function which accepts a list of sequence in which each sequence will be used in for comprehension expression.

eg.

for (x <- (1 to 10); y <- (1 to 10)) yield List(x,y)

In the above expression, I must know before hand that I want a combination of x and y. WHat if I want a combination of x, y, z ...etc... (an unknown number of combination)? If I want 10 combinations, I want to paste 10 snippets "x <- (1 to 10)" into the expression. I think I can do this with macro in Clojure (a way to paste code). How can I do this in Scala?

The function I want to write has the signature like this:

combine(list: List[List[Int]])

The body of the function will use each item in the list to paste into the for comprehension.

Hope you understand my intention.

2 Answers2

2

If you want to compute the cartesian product of a list of lists, you can do it by chaining map and flatMap calls together recursively. That is what for expressions do anyway.

  def prod(l:List[List[Int]]):List[List[Int]] = l match {
    case Nil => List(Nil)
    case l::ls => l.flatMap(i => prod(ls).map(t =>i::t))
  }
Kim Stebel
  • 41,826
  • 12
  • 125
  • 142
  • The line `case l::ls ...` could be rewritten as `case (l :: ls) => for(i <- l; r <- prod(ls)) yield (i :: r)` using `for` comprehension. This is slightly closer to what the question used and perhaps easier to understand. – Petr Sep 23 '12 at 16:37
  • I think that Petr's solution is also demonstrates that inefficient flatmap is not necesary. It seems to me that fltamap waits for the result and then merges two lists whereas if you simply append single elements in the yield is more efficient! – Val Jul 08 '14 at 17:41
1

You know how to combine two things into one thing, and you want to combine an arbitrary number, so that means you need a fold. The only modification is that you want to start with a list of singletons, not a list of items:

val xss = List(List(1,2,3),List(4,5),List(6,7))
val singletons = xss.head.map(x => List(x))   // List(List(1), ...)
val seqs = (singletons /: xss.tail){ (yss, xs) => for (ys <- yss; x <- xs) yield x :: ys }
seqs.map(_.reverse)

which at the last stage prints out

List(List(1, 4, 6), List(1, 4, 7), List(1, 5, 6),
     List(1, 5, 7), List(2, 4, 6), List(2, 4, 7),
     List(2, 5, 6), List(2, 5, 7), List(3, 4, 6),
     List(3, 4, 7), List(3, 5, 6), List(3, 5, 7))
Rex Kerr
  • 166,841
  • 26
  • 322
  • 407