函数接受两个List [Int]参数,并产生一个List [Int]。斯卡拉

时间:2019-04-06 10:13:20

标签: scala list recursion

结果列表的元素应在参数的元素之间交替。假设两个参数的长度相同。

使用恢复

我的代码如下

val finalString = new ListBuffer[Int]
val buff2= new ListBuffer[Int]
def alternate(xs:List[Int], ys:List[Int]):List[Int] = {
    while (xs.nonEmpty) {
        finalString += xs.head + ys.head
        alternate(xs.tail,ys.tail)
    }
    return finalString.toList
}

预期结果:

alternate(列表(1、3、5),列表(2、4、6))=列表(1、2、3、4、6)

就输出而言,我没有任何输出。该程序仍在运行,无法执行。

有Scala专家吗?

6 个答案:

答案 0 :(得分:4)

到目前为止,建议的递归解决方案存在一些问题(包括您的递归解决方案,如果您将while替换为if的话,这实际上是可行的):追加到列表末尾是线性操作,使整个事情都是二次的(也取列表的.length,以及按索引访问元素),不要那样做;另外,如果列表很长,则递归可能会在堆栈上占用很多空间,因此您应该尽可能使用tail递归。

这是一个没有这两个问题的解决方案:它通过向列表中添加前置元素(恒定时间操作)而不是附加它们,来向后构建输出,并在结束。它也是尾递归的:递归调用是函数中的最后一个操作,它允许编译器将其转换为循环,因此无论列表大小如何,它都将仅使用单个堆栈框架执行。

  @tailrec
  def alternate(
    a: List[Int], 
    b: List[Int], 
    result: List[Int] = Nil
  ): List[Int] = (a,b) match {
    case (Nil, _) | (_, Nil) => result.reversed
    case (ah :: at, bh :: bt) => alternate(at, bt, bh :: ah :: result)
  }

(如果列表的长度不同,则最短的列表结束时整个过程将停止,而较长的列表中的所有内容将被丢弃。您可能需要修改第一个case(将其拆分为两个,也许)如果您想要改变行为)。

顺便说一句,您自己的解决方案实际上比这里的大多数建议更好:它实际上是尾递归的(或者,如果在else之后添加if,则可以将其设为尾递归,现在为while),并附加到ListBuffer上实际上并不比List差。但是使用可变状态通常在Scala中被认为是“代码异味”,应避免使用(这是使用递归而不是循环的主要思想之一)。

答案 1 :(得分:2)

条件xs.nonEmpty始终为真,因此您有无限的while循环。

也许您是说if而不是while

答案 2 :(得分:2)

更像Scala式的方法是:

def alternate(xs: List[Int], ys: List[Int]): List[Int] = {
  xs.zip(ys).flatMap{case (x, y) => List(x, y)}
}

alternate(List(1,3,5), List(2,4,6))
// List(1, 2, 3, 4, 5, 6)

答案 3 :(得分:1)

使用match

的递归解决方案
def alternate[T](a: List[T], b: List[T]): List[T] =
  (a, b) match {
    case (h1::t1, h2::t2) =>
      h1 +: h2 +: alternate(t1, t2)
    case _ =>
      a ++ b
  }

这可能会更清晰,但会更加清晰。


更新

这是更有效的解决方案:

def alternate[T](a: List[T], b: List[T]): List[T] = {
  @annotation.tailrec
  def loop(a: List[T], b: List[T], res: List[T]): List[T] =
    (a, b) match {
      case (h1 :: t1, h2 :: t2) =>
        loop(t1, t2, h2 +: h1 +: res)
      case _ =>
        a ++ b ++ res.reverse
    }

  loop(a, b, Nil)
}

这保留了原始函数签名,但使用的是内部函数,该函数是算法的高效尾递归实现。

答案 4 :(得分:0)

您正在从方法外部访问变量,这很糟糕。我建议如下所示:

object Main extends App {
    val l1 = List(1, 3, 5)
    val l2 = List(2, 4, 6)

    def alternate[A](l1: List[A], l2: List[A]): List[A] = {
        if (l1.isEmpty || l2.isEmpty) List()
        else List(l1.head,l2.head) ++ alternate(l1.tail, l2.tail)
    }

    println(alternate(l1, l2))
}

仍然是递归的,但是没有从方法外部访问状态。

答案 5 :(得分:-1)

假设两个列表的长度相同,则可以使用ListBuffer来构建备用列表。 alternate是一个纯函数:

import scala.collection.mutable.ListBuffer

object Alternate extends App {
  def alternate[T](xs: List[T], ys: List[T]): List[T] = {
    val buffer = new ListBuffer[T]

    for ((x, y) <- xs.zip(ys)) {
      buffer += x
      buffer += y
    }

    buffer.toList
  }

  alternate(List(1, 3, 5), List(2, 4, 6)).foreach(println)
}