Scala 方法将可迭代的每个元素与另一个可迭代的每个元素组合起来?

发布于 2024-11-06 03:16:28 字数 351 浏览 7 评论 0原文

如果我有这个:

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

我想知道是否存在这样的方法,它可以让我遍历第一个集合,并对于它的每个元素,遍历整个第二个集合。例如,如果我们采用数组 a,我们将有 a,x,a,y,b,x,<代码>b,y,<代码>c,x,<代码>c,y。我知道 zip 但据我所知它只适用于相同大小的集合,并且它关联来自相同位置的元素。

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 me traverse the first collection, and for each of it's elements, walk the entire second collection. For example, if we take the array a, we would have a,x,a,y,b,x,b,y,c,x,c,y. I know of zip but from what I've seen it only works on collections of the same sizes, and it associates elements from same positions.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

过期以后 2024-11-13 03:16:28

我不确定“方法”,但这可以用嵌套/复合 for 来表达:

val a = Array("a ","b ","c ")
val b = Array("x","y")
for (a_ <- a; b_ <- b) yield (a_, b_)

res0: Array[(java.lang.String, java.lang.String)] = Array((a ,x), (a ,y), (b ,x), (b ,y), (c ,x), (c ,y))

快乐编码。

I'm not sure of a "method", but this can be expressed with just a nested/compound for:

val a = Array("a ","b ","c ")
val b = Array("x","y")
for (a_ <- a; b_ <- b) yield (a_, b_)

res0: Array[(java.lang.String, java.lang.String)] = Array((a ,x), (a ,y), (b ,x), (b ,y), (c ,x), (c ,y))

Happy coding.

邮友 2024-11-13 03:16:28

对于未知数量的列表、不同长度的列表以及可能不同类型的列表,您可以使用以下方法:

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
}

您可以调用它,

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

但也可以使用不同类型的列表来调用它:

scala>  xproduct (List (List ("Beatles", "Stones"), List (8, 9, 10), List ('

数组必须转换为列表,并且如果不能使用列表,结果将转换回数组。

更新:

在走向惰性集合的过程中,我做了一个从索引(从 0 到组合大小 - 1)到该位置结果的函数映射,可以使用模和除法轻松计算,只需要一点集中

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

:使用 long 甚至 BigInt 都没有问题。

更新2、迭代器:

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) 
  }
}
, '€'))) 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, €))

数组必须转换为列表,并且如果不能使用列表,结果将转换回数组。

更新:

在走向惰性集合的过程中,我做了一个从索引(从 0 到组合大小 - 1)到该位置结果的函数映射,可以使用模和除法轻松计算,只需要一点集中

:使用 long 甚至 BigInt 都没有问题。

更新2、迭代器:

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 ('

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) 
  }
}
, '€'))) 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:

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

update 2, The iterator:

娇女薄笑 2024-11-13 03:16:28

我在我的代码中广泛使用以下内容。请注意,这适用于任意数量的列表。它创建一个迭代器而不是集合,因此您不必将潜在的巨大结果存储在内存中。

任何改进都是非常受欢迎的。

/**
  * An iterator, that traverses every combination of objects in a List of Lists.
  * The first Iterable will be incremented fastest. So consider the head as 
  * the "least significant" bit when counting.*/

class CombinationIterator[A](val components: List[Iterable[A]]) extends Iterator[List[A]]{
  private var state: List[BufferedIterator[A]] = components.map(_.iterator.buffered)
  private var depleted = state.exists(_.isEmpty)

  override def next(): List[A] = {
    //this function assumes, that every iterator is non-empty    
    def advance(s: List[(BufferedIterator[A],Iterable[A])]): List[(BufferedIterator[A],A)] = {
      if( s.isEmpty ){
        depleted = true
        Nil
      }
      else {
        assert(!s.head._1.isEmpty)

        //advance and return identity
        val it = s.head._1
        val next = it.next()
        if( it.hasNext){
          //we have simply incremented the head, so copy the rest
          (it,next) :: s.tail.map(t => (t._1,t._1.head))
        } else {
          //we have depleted the head iterator, reset it and increment the rest
          (s.head._2.iterator.buffered,next) :: advance(s.tail)
        }
      }
    }
    //zipping the iterables to the iterators is needed for resseting them
    val (newState, result) = advance(state.zip(components)).unzip
    
    //update state
    state = newState    
    
    result
  }

  override def hasNext = !depleted
}

因此,使用这个迭代器,您必须编写 new CombinationIterator(List(a,b)) 来获取遍历每个组合的迭代器。

编辑:基于用户未知的版本

请注意,以下版本不是最佳的(性能方面):

  • 对列表进行索引访问(改为使用数组)
  • takeWhile 在每个 element 之后进行评估

scala> 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)
     | }
combination: (xx: List[List[_]], i: Int)List[_]

scala> def combinationIterator(ll: List[List[_]]): Iterator[List[_]] = {
     | Iterator.from(0).takeWhile(n => n < ll.map(_.length).product).map(combination(ll,_))
     | }
combinationIterator: (ll: List[List[_]])Iterator[List[_]]

scala> List(List(1,2,3),List("a","b"),List(0.1,0.2,0.3))
res0: List[List[Any]] = List(List(1, 2, 3), List(a, b), List(0.1, 0.2, 0.3))
    
scala> combinationIterator(res0)
res1: Iterator[List[_]] = non-empty iterator

scala> res1.mkString("\n")
res2: String = 
List(1, a, 0.1)
List(2, a, 0.1)
List(3, a, 0.1)
List(1, b, 0.1)
List(2, b, 0.1)
List(3, b, 0.1)
List(1, a, 0.2)
List(2, a, 0.2)
List(3, a, 0.2)
List(1, b, 0.2)
List(2, b, 0.2)
List(3, b, 0.2)
List(1, a, 0.3)
List(2, a, 0.3)
List(3, a, 0.3)
List(1, b, 0.3)
List(2, b, 0.3)
List(3, b, 0.3)

I'm using the following extensively in my code. Note that this is working for an arbitrary number of lists. It is creating an Iterator instead of a collection, so you don't have to store the potentially huge result in memory.

Any improvements are very welcome.

/**
  * An iterator, that traverses every combination of objects in a List of Lists.
  * The first Iterable will be incremented fastest. So consider the head as 
  * the "least significant" bit when counting.*/

class CombinationIterator[A](val components: List[Iterable[A]]) extends Iterator[List[A]]{
  private var state: List[BufferedIterator[A]] = components.map(_.iterator.buffered)
  private var depleted = state.exists(_.isEmpty)

  override def next(): List[A] = {
    //this function assumes, that every iterator is non-empty    
    def advance(s: List[(BufferedIterator[A],Iterable[A])]): List[(BufferedIterator[A],A)] = {
      if( s.isEmpty ){
        depleted = true
        Nil
      }
      else {
        assert(!s.head._1.isEmpty)

        //advance and return identity
        val it = s.head._1
        val next = it.next()
        if( it.hasNext){
          //we have simply incremented the head, so copy the rest
          (it,next) :: s.tail.map(t => (t._1,t._1.head))
        } else {
          //we have depleted the head iterator, reset it and increment the rest
          (s.head._2.iterator.buffered,next) :: advance(s.tail)
        }
      }
    }
    //zipping the iterables to the iterators is needed for resseting them
    val (newState, result) = advance(state.zip(components)).unzip
    
    //update state
    state = newState    
    
    result
  }

  override def hasNext = !depleted
}

So using this one, you have to write new CombinationIterator(List(a,b)) to obtain an iterator that goes through every combination.

Edit: based on user unkown's version

Note that the following version is not optimal (performance wise):

  • indexed access into lists (use arrays instead)
  • takeWhile evaluates after every element

.

scala> 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)
     | }
combination: (xx: List[List[_]], i: Int)List[_]

scala> def combinationIterator(ll: List[List[_]]): Iterator[List[_]] = {
     | Iterator.from(0).takeWhile(n => n < ll.map(_.length).product).map(combination(ll,_))
     | }
combinationIterator: (ll: List[List[_]])Iterator[List[_]]

scala> List(List(1,2,3),List("a","b"),List(0.1,0.2,0.3))
res0: List[List[Any]] = List(List(1, 2, 3), List(a, b), List(0.1, 0.2, 0.3))
    
scala> combinationIterator(res0)
res1: Iterator[List[_]] = non-empty iterator

scala> res1.mkString("\n")
res2: String = 
List(1, a, 0.1)
List(2, a, 0.1)
List(3, a, 0.1)
List(1, b, 0.1)
List(2, b, 0.1)
List(3, b, 0.1)
List(1, a, 0.2)
List(2, a, 0.2)
List(3, a, 0.2)
List(1, b, 0.2)
List(2, b, 0.2)
List(3, b, 0.2)
List(1, a, 0.3)
List(2, a, 0.3)
List(3, a, 0.3)
List(1, b, 0.3)
List(2, b, 0.3)
List(3, b, 0.3)
江南烟雨〆相思醉 2024-11-13 03:16:28

如果你想展示你对更高种类类型和范畴论的深入了解,你可以写:

trait Applicative[App[_]] {
  def pure[A](a: A): App[A]
  def fmap[A,B](f: A => B, appA: App[A]): App[B]
  def star[A,B](appF: App[A => B], appA: App[A]): App[B]
}

object ListApplicative extends Applicative[List] {
  override def pure[A](a: A): List[A] = List(a)
  override def fmap[A,B](f: A => B, listA: List[A]): List[B] = listA.map(f)
  override def star[A,B](listF: List[A => B], listA: List[A]):List[B] = 
    for(f <- listF; a <- listA) yield f(a)
}

import ListApplicative._

def pairs[A,B](listA: List[A], listB: List[B]) = 
  star(fmap((a:A) => ((b:B) => (a,b)), listA), listB)

除此之外,我更喜欢 pst 的解决方案......

If you want to show off your deep knowledge of higher kinded types and category theory, you can write:

trait Applicative[App[_]] {
  def pure[A](a: A): App[A]
  def fmap[A,B](f: A => B, appA: App[A]): App[B]
  def star[A,B](appF: App[A => B], appA: App[A]): App[B]
}

object ListApplicative extends Applicative[List] {
  override def pure[A](a: A): List[A] = List(a)
  override def fmap[A,B](f: A => B, listA: List[A]): List[B] = listA.map(f)
  override def star[A,B](listF: List[A => B], listA: List[A]):List[B] = 
    for(f <- listF; a <- listA) yield f(a)
}

import ListApplicative._

def pairs[A,B](listA: List[A], listB: List[B]) = 
  star(fmap((a:A) => ((b:B) => (a,b)), listA), listB)

Other than that I would prefer pst's solution...

爱她像谁 2024-11-13 03:16:28

这是另一个与 @ziggystar 的上次编辑相同的事情,但不使用列表的索引访问。

def combinationIterator[A](xs: Iterable[Iterable[A]]): Iterator[List[A]] = {
  xs.foldRight(Iterator(List[A]())) { (heads, tails) =>
    tails.flatMap { tail =>
      heads.map(head => head :: tail)
    }
  }
}

还有含糖版本:

def combinationIterator[A](xs: Iterable[Iterable[A]]): Iterator[List[A]] = {
  (xs :\ Iterator(List[A]())) { (heads, tails) =>
    for (tail <- tails; head <- heads) yield head :: tail
  }
}

Here's one more that does the same thing as @ziggystar's last edit but doesn't use indexed access of lists.

def combinationIterator[A](xs: Iterable[Iterable[A]]): Iterator[List[A]] = {
  xs.foldRight(Iterator(List[A]())) { (heads, tails) =>
    tails.flatMap { tail =>
      heads.map(head => head :: tail)
    }
  }
}

And the sugary version:

def combinationIterator[A](xs: Iterable[Iterable[A]]): Iterator[List[A]] = {
  (xs :\ Iterator(List[A]())) { (heads, tails) =>
    for (tail <- tails; head <- heads) yield head :: tail
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文