Scala method to combine each element of an iterable with each element of another?

后端 未结 5 1399
情深已故
情深已故 2020-12-06 11:53

If I have this:

val a = Array(\"a \",\"b \",\"c \")
val b = Array(\"x\",\"y\")

I would like to know if such a method exists which would let

5条回答
  •  执笔经年
    2020-12-06 12:24

    For a list of a unknown number of lists, of different length, and for maybe different types, you can use this:

    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
    }
    

    You would call it

    xproduct List (List ("a ", "b ", "c "), List ("x", "y"))
    

    but can call it with Lists of different kind too:

    scala>  xproduct (List (List ("Beatles", "Stones"), List (8, 9, 10), List ('$', '€')))  
    res146: List[List[_]] = List(List(Beatles, 8, $), List(Stones, 8, $), List(Beatles, 8, €), List(Stones, 8, €), List(Beatles, 9, $), List(Stones, 9, $), List(Beatles, 9, €), List(Stones, 9, €), List(Beatles, 10, $), List(Stones, 10, $), List(Beatles, 10, €), List(Stones, 10, €))
    

    Arrays have to be converted to Lists, and the result converted back to Arrays, if you can't use Lists.

    update:

    On the way towards a lazy collection, I made a functional mapping from an index (from 0 to combination-size - 1) to the result at that position, easily calculated with modulo and division, just a bit concentration is needed:

    def combicount (xx: List [List[_]]): Int = (1 /: xx) (_ * _.length)
    
    def combination (xx: List [List[_]], i: Int): List[_] = xx match {
        case Nil => Nil
        case x :: xs => x(i % x.length) :: combination (xs, i / x.length)
    }
    
    def xproduct (xx: List [List[_]]): List [List[_]] = 
      (0 until combicount (xx)).toList.map (i => combination (xx, i))
    

    It's no problem to use a long instead, or even BigInt.

    update 2, The iterator:

    class Cartesian (val ll: List[List[_]]) extends Iterator [List[_]] {
    
      def combicount (): Int = (1 /: ll) (_ * _.length)
    
      val last = combicount - 1 
      var iter = 0
      
      override def hasNext (): Boolean = iter < last
      override def next (): List[_] = {
        val res = combination (ll, iter)
        iter += 1
        res
      }
    
      def combination (xx: List [List[_]], i: Int): List[_] = xx match {
          case Nil => Nil
          case x :: xs => x (i % x.length) :: combination (xs, i / x.length) 
      }
    }
    

提交回复
热议问题