Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Idiomatic Scala way of generating combinations lazily

I want to generate a combination of some values as in the code below:

object ContinueGenerate {

  val foods = List("A", "B", "C")
  val places = List("P1", "P2", "P3")
  val communities = List("C1", "C2", "C3", "C4")

  case class Combination(food: String, place: String, community: String)

  def allCombinations() = {
    for {
      food <- foods; place <- places; community <- communities
    } yield Combination(food, place, community)
  }

  def main(args: Array[String]) {
    allCombinations foreach println
  }

}

However the problem with this approach is that, all the data is generated at once. This is big problem when the size of foods, places and communities becomes very large. Also there could be other parameters other than these three.

So I want to be able generate combinations, in a continuation style, such that a combination is generated only when it is requested.

What would be an idiomatic Scala way of doing that ?

like image 748
tuxdna Avatar asked Aug 21 '15 07:08

tuxdna


2 Answers

You use streams:

object ContinueGenerate {

  val foods = Stream("A", "B", "C")
  val places = Stream("P1", "P2", "P3")
  val communities = Stream("C1", "C2", "C3", "C4")

  case class Combination(food: String, place: String, community: String)

  def allCombinations() = {
    for {
      food <- foods; place <- places; community <- communities
    } yield Combination(food, place, community)
  }

  def main(args: Array[String]) {
    allCombinations foreach println
  }

}

A Stream caches all the data. If you only want to iterate once, use Iterator instead, which should garbage-collect already traversed elements.

like image 191
Reactormonk Avatar answered Dec 15 '22 00:12

Reactormonk


You can do this by using a View over each list. In the code below I've added a side effect so it is visible when the yield gets called for each element.

val foods = List("A", "B", "C")
val places = List("P1", "P2", "P3")
val communities = List("C1", "C2", "C3", "C4")

case class Combination(food: String, place: String, community: String)

def allCombinations() =
  for {
    food <- foods; place <- places; community <- communities
  } yield {
    val comb = Combination(food, place, community)
    println(comb)
    comb
  }

//Prints all items
val combinations = allCombinations()

def allCombinationsView() =
  for {
    //Use a view of each list
    food <- foods.view; place <- places.view; community <- communities.view
  } yield {
    val comb = Combination(food, place, community)
    println(comb)
    comb
  }
//Prints nothing
val combinationsView = allCombinationsView()

//Prints 5 items
val five = combinationsView.take(5).toList
like image 35
mattinbits Avatar answered Dec 14 '22 23:12

mattinbits