我怎样才能在scala中交错2个列表的元素

时间:2012-07-05 18:27:49

标签: scala scalaz

我想组合两个任意长度的列表,使得第二个列表中的元素在每个第n个元素之后插入到第一个列表中。如果第一个列表长度小于n,则不会产生插入结果。

所以

val a = List(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15)
val b = List(101,102,103)
val n = 3 

我希望结果列表看起来像这样:

List(1,2,3,101,4,5,6,102,7,8,9,103,10,11,12,13,14,15)

我在foldLeft上使用a工作,但我想知道如何使用Scalaz完成相同的逻辑?

感谢大家的回答。他们对我都有帮助!

7 个答案:

答案 0 :(得分:6)

认识我的apomorphism朋友

def apo[A, B](v: B)(f: B => Option[(A, Either[B, List[A]])]): List[A] = f(v) match {
   case None => Nil
   case Some((a, Left(b)))   => a :: apo(b)(f)
   case Some((a, Right(as))) => a :: as 
}

您的交错方法可以像这样实现

def interleave[A](period: Int, substitutes: List[A], elems: List[A]): List[A] =
  apo((period, substitutes, elems)){
    case (_, _, Nil)       => None
    case (_, Nil, v :: vs) => Some((v, Right(vs)))
    case (0, x :: xs, vs)  => Some((x, Left((period, xs, vs))))
    case (n, xs, v :: vs)  => Some((v, Left((n - 1, xs, vs))))  
  }

这给出了:

scala> interleave(3, b, a)
res1: List[Int] = List(1, 2, 3, 101, 4, 5, 6, 102, 7, 8, 9, 103 , 10, 11 , 12, 13, 14, 15)

好处是,当a或b为Nil时计算结束,与foldLeft不同。坏消息是交错不再是尾递归

答案 1 :(得分:4)

这个怎么样:

 def process[A](xs: List[A], ys: List[A], n: Int): List[A] = 
   if(xs.size <= n || ys.size == 0) xs
   else xs.take(n):::ys.head::process(xs.drop(n),ys.tail,n)

scala> process(a,b,n) 
res8: List[Int] = List(1, 2, 3, 101, 4, 5, 6, 102, 7, 8, 9, 103, 10, 11, 12, 13, 14, 15)

scala> val a = List(1,2,3,4,5,6,7,8,9,10,11) 
a: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)

scala> process(a,b,n) 
res9: List[Int] = List(1, 2, 3, 101, 4, 5, 6, 102, 7, 8, 9, 103, 10, 11)

scala> val a = List(1,2,3,4,5,6,7,8,9) 
a: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)

scala> process(a,b,n) 
res10: List[Int] = List(1, 2, 3, 101, 4, 5, 6, 102, 7, 8, 9)

scala> val a = List(1,2,3,4,5,6,7,8) 
a: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8)

scala> process(a,b,n) 
res11: List[Int] = List(1, 2, 3, 101, 4, 5, 6, 102, 7, 8)

您的请求是“如果第一个列表长度小于n,没有插入结果”,那么我的代码应该更改为:

 def process[A](xs: List[A], ys: List[A], n: Int): List[A] = 
   if(xs.size < n || ys.size == 0) xs
   else xs.take(n):::ys.head::process(xs.drop(n),ys.tail,n)

答案 2 :(得分:4)

使用zipAll变得非常简单。此外,您可以选择第二个数组的元素数量(在本例中为1):

val middle = b.grouped(1).toList
val res = a.grouped(n).toList.zipAll(middle, Nil, Nil)
res.filterNot(_._1.isEmpty).flatMap(x => x._1 ++ x._2)

或者如果您愿意,可以单行:

a.grouped(n).toList.zipAll(b.map(List(_)), Nil, Nil).filterNot(_._1.isEmpty).flatMap(x => x._1 ++ x._2)

您也可以创建一个隐式类,因此您可以调用a.interleave(b, 3)或使用可选的thrid参数a.interleave(b, 3, 1)

答案 3 :(得分:3)

怎么样:

def interleave[A](xs: Seq[A], ys: Seq[A], n: Int): Seq[A] = {
  val iter = xs grouped n
  val coll = iter zip ys.iterator flatMap { case (xs, y) => if (xs.size == n) xs :+ y else xs }
  (coll ++ iter.flatten).toIndexedSeq
}

scala> interleave(a, b, n)
res34: Seq[Int] = Vector(1, 2, 3, 101, 4, 5, 6, 102, 7, 8, 9, 103, 10, 11, 12, 13, 14, 15)

scala> interleave(1 to 2, b, n)
res35: Seq[Int] = Vector(1, 2)

scala> interleave(1 to 6, b, n)
res36: Seq[Int] = Vector(1, 2, 3, 101, 4, 5, 6, 102)

scala> interleave(1 to 7 b, n)
res37: Seq[Int] = Vector(1, 2, 3, 101, 4, 5, 6, 102, 7)

scala> interleave(1 to 7, Nil, n)
res38: Seq[Int] = Vector(1, 2, 3, 4, 5, 6, 7)

scala> interleave(1 to 7, Nil, -3)
java.lang.IllegalArgumentException: requirement failed: size=-3 and step=-3, but both must be positive

它很短,但它不是最有效的解决方案。例如,如果您使用列表调用它,则追加操作(:+++)非常昂贵(O(n))。

编辑:对不起。我现在注意到,你想要一个Scalaz的解决方案。然而,答案可能有用,因此我不会删除它。

答案 4 :(得分:3)

没有Scalaz和递归。

scala> a.grouped(n).zip(b.iterator.map{ Some(_) } ++ Iterator.continually(None)).flatMap{ case (as, e) => if (as.size == n) as ++ e else as }.toList
res17: List[Int] = List(1, 2, 3, 101, 4, 5, 6, 102, 7, 8, 9, 103, 10, 11, 12, 13, 14, 15)

通用方式:

def filled[T, A, That](a: A, b: Seq[T], n: Int)(implicit bf: CanBuildFrom[A, T, That], a2seq: A => Seq[T]): That = {
  val builder = bf()
  builder.sizeHint(a, a.length / n)
  builder ++= a.grouped(n).zip(b.iterator.map{ Some(_) } ++ Iterator.continually(None)).flatMap{ case (as, e) => if(as.size == n ) as ++ e else as }
  builder.result()
}

用法:

scala> filled("abcdefghijklmnopqrstuvwxyz", "1234", 3)
res0: String = abc1def2ghi3jkl4mnopqrstuvwxyz

scala> filled(1 to 15, 101 to 103, 3)
res1: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 101, 4, 5, 6, 102, 7, 8, 9, 103, 10, 11, 12, 13, 14, 15)

scala> filled(1 to 3, 101 to 103, 3)
res70: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 101)

scala> filled(1 to 2, 101 to 103, 3)
res71: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2)

答案 5 :(得分:0)

我认为这是最简单的解决方案。

val bi = b.toIterator
a.zipWithIndex.flatMap {case (el, i) =>
    if (bi.hasNext && (i-1)%n == n-1) List(bi.next, el) else List(el)
}

答案 6 :(得分:0)

这是你想要的那个:

&#xA;&#xA;
  import scala.annotation.tailrec&#xA;&#xA; @ tailrec&#xA; final def interleave [A ](base:Vector [A],a:List [A],b:List [A]):Vector [A] =匹配{&#xA; case elt :: aTail =&gt; interleave(base:+ elt,b,aTail)&#xA; case _ =&gt; base ++ b&#xA;}&#xA;&#xA; ...&#xA;&#xA; interleave(Vector.empty,a,b)&#xA;  
& #xA;