【问题标题】:How can i interleave elements of 2 lists in scala如何在scala中交错2个列表的元素
【发布时间】:2012-07-05 18:27:49
【问题描述】:

我想以这样的方式组合两个任意长度的列表,即第二个列表中的元素在每个第 n 个元素之后插入到第一个列表中。如果第一个 List 长度小于 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)

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

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

【问题讨论】:

    标签: scala scalaz


    【解决方案1】:

    认识我的异形朋友

    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 不同。坏消息是交错不再是尾递归

    【讨论】:

    • 有趣。如果我正确理解了这个术语,那么自言自语和尾递归是一种互斥的?
    • 对。您可能拥有拥有“数千个”列表的 SOE。注意你的例子,apomorphism 比 catamorphism (foldLeft) 更有效,因为它会在 b list 变为 Nil 之后停止
    【解决方案2】:

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

    【讨论】:

    • 我又发布了一个替代答案。
    • 似乎是最干净的 - 与折叠或直接递归相比,这有什么陷阱吗?我认为就性能而言,它可能介于折叠和递归之间
    【解决方案3】:

    这个怎么样:

     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)
    

    【讨论】:

      【解决方案4】:

      怎么样:

      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 找到解决方案。不过答案可能有用,所以我不会删除它。

      【讨论】:

      • 如果 a.size > 3(b.size+1),您的解决方案无法正常运行
      • @Antoras:是的,我一直在寻找比使用foldLeft 更优雅的解决方案。你的适用于这个例子。但是,选择一个不好的例子是我的错,列表可以是任意长度。所以a.grouped(n).length 不一定等于b.length
      • @marcin:稍微改变了我的代码。希望它现在可以按预期工作。
      【解决方案5】:

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

      【讨论】:

      • map+flattenflatMap 相同
      • @senia:我喜欢使用 None 的填充,聪明。但是,如果a.length 小于 n,则会发生插入。我希望它仅在 a.length &gt;= n 时发生
      • @marcin,很难通过组合来表达如此复杂的逻辑。在这种情况下,IMO 最好使用递归/循环。
      【解决方案6】:

      这是你想要的:

      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)
      

      【讨论】:

        猜你喜欢
        • 2011-04-27
        • 1970-01-01
        • 1970-01-01
        • 2023-03-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-01-21
        相关资源
        最近更新 更多