8

我想以这样的方式组合两个任意长度的列表,即第二个列表中的元素在每个第 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)

我使用foldLefton进行了这项工作a,但我想知道如何使用 Scalaz 完成相同的逻辑?

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

4

6 回答 6

8

Meet my apomorphism friend

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 
}

Your interleave method can be implemented like this

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

This gives:

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)

The good point is the computation ends when a or b are Nil unlike foldLeft. The bad news is interleave is no more tail recursive

于 2012-07-06T10:48:41.247 回答
5

这变得非常简单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)或使用可选的第三个参数a.interleave(b, 3, 1)

于 2014-06-14T16:00:15.847 回答
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)
于 2012-07-06T03:08:17.427 回答
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)
于 2012-07-06T05:52:24.960 回答
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

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

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

于 2012-07-05T20:50:03.730 回答
0

这是你想要的:

import scala.annotation.tailrec

@tailrec
final def interleave[A](base: Vector[A], a: List[A], b: List[A]): Vector[A] = a match {
  case elt :: aTail => interleave(base :+ elt, b, aTail)
  case _ => base ++ b
}

...

interleave(Vector.empty, a, b)
于 2017-03-23T18:19:07.830 回答