【问题标题】:How to change the functional insert-sort code to be tail recursive如何将功能插入排序代码更改为尾递归
【发布时间】:2015-02-04 16:12:50
【问题描述】:

最近我用函数式编程风格实现了insert_sort算法,它变得更加简洁和声明性。问题是如何将其更改为尾递归,如果列表的大小增长到 10000,代码将抛出异常。

def InsertSort(xs: List[Int]): List[Int] = xs match {
    case Nil => Nil
    case x::rest => 
       def insert (x: Int, sorted_xs:List[Int]) :List[Int] = sorted_xs match{
           case Nil => List(x)
           case y::ys => if  (x <= y) x::y::ys else y::insert(x,ys)
       }
       insert(x,InsertSort(rest))
 }

【问题讨论】:

    标签: scala recursion tail-recursion insertion-sort


    【解决方案1】:

    刚刚介绍了累加器:

     @tailrec def InsertSort(xs: List[Int], acc: List[Int] = Nil): List[Int] = 
      if (xs.nonEmpty) {
        val x :: rest = xs
        @tailrec 
        def insert(x: Int, sorted_xs: List[Int], acc: List[Int] = Nil): List[Int] =
          if (sorted_xs.nonEmpty) { 
            val y :: ys = sorted_xs
            if (x <= y) acc ::: x :: y :: ys else insert(x,ys, acc :+ y)
          } else acc ::: List(x)
        InsertSort(rest, insert(x, acc))
      } else acc
    

    ::::+ 对于默认的 List 实现将采用 O(n),因此最好使用一些更合适的集合(如 ListBuffer)。你也可以用foldLeft 重写它而不是显式递归。

    更快的选项(带foldLeft,不带:+):

     @tailrec
     def insert(sorted_xs: List[Int], x: Int, acc: List[Int] = Nil): List[Int] =
       if (sorted_xs.nonEmpty) { 
         val y::ys = sorted_xs
         if (x <= y) acc.reverse ::: x :: y :: ys else insert(ys, x, y :: acc)
       } else (x :: acc).reverse
    
     scala> List(1,5,3,6,9,6,7).foldLeft(List[Int]())(insert(_, _))
     res22: List[Int] = List(1, 3, 5, 6, 6, 7, 9)
    

    最后是span(就像在@roterl 的回答中一样,但span 快一点——它只遍历集合直到找到&gt; x):

     def insert(sorted_xs: List[Int], x: Int) = if (sorted_xs.nonEmpty) { 
        val (smaller, larger) = sorted_xs.span(_ < x)
        smaller ::: x :: larger
     } else x :: Nil
    
     scala> List(1,5,3,6,9,6,7).foldLeft(List[Int]())(insert)
     res25: List[Int] = List(1, 3, 5, 6, 6, 7, 9)
    

    【讨论】:

    • 这是我自己能想到的唯一解决方案。但是这段代码几乎不可读。特别是如果您将其与命令式替代方案进行比较。我想知道是否有人知道更优雅的功能解决方案。或者这是否证明了函数式编程的表达能力有其局限性?
    • 我添加了 foldLeft 选项 - 看起来不错
    • 谢谢。您的 foldleft 版本更具表现力,但也更加抽象。
    【解决方案2】:

    要使其尾递归,您应该将排序列表作为参数传递,而不是在返回值处构建它:

    def InsertSort(xs: List[Int]): List[Int] = {
      @tailrec
      def doSort(unsortXs: List[Int], sorted_xs: List[Int]): List[Int] = {
        unsortXs match {
          case Nil => sorted_xs
          case x::rest => 
            val (smaller, larger) = sorted_xs.partition(_ < x)
            doSort(rest, smaller ::: x :: larger)
        }
      }
      doSort(xs, List())  
    }
    

    【讨论】:

    • 'partition' 只是替换了这里的 Insert 方法。 @dk14 的答案更完整。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-14
    • 2019-01-10
    • 2020-05-05
    • 2014-10-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多