如果我有这个:
val a = Array("a ","b ","c ")
val b = Array("x","y")
我想知道是否存在允许我遍历第一个集合的这种方法,并且对于每个元素,遍历整个第二个集合。例如,如果我们采用数组a
,我们会a,x
,a,y
,b,x
,b,y
,c,x
,{{1 }}。我知道拉链,但从我看到它只适用于相同尺寸的系列,并且它关联来自相同位置的元素。
答案 0 :(得分:24)
我不确定“方法”,但这可以用嵌套/复合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))
快乐的编码。
答案 1 :(得分:6)
对于未知数量的列表,不同长度以及可能不同类型的列表,您可以使用:
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 ('$', '€')))
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)到该位置的结果进行了功能映射,可以使用模数和除法进行计算,只需要集中注意力:
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都没问题。
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)
}
}
答案 2 :(得分:5)
我在我的代码中广泛使用以下内容。请注意,这适用于任意数量的列表。它正在创建一个Iterator而不是一个集合,因此您不必将潜在的巨大结果存储在内存中。
非常欢迎任何改进。
/**
* 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))
来获得一个遍历每个组合的迭代器。
请注意,以下版本不是最佳版本(性能明智):
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)
答案 3 :(得分:4)
如果你想展示你对更高知识类型和范畴理论的深刻了解,你可以写:
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的解决方案......
答案 4 :(得分:0)
此处还有一个与@ 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
}
}