2012-02-12 125 views
4

我写了一个递归版本:如何实现尾递归快速排序在斯卡拉

def quickSort[T](xs: List[T])(p: (T, T) => Boolean): List[T] = xs match{ 
    case Nil => Nil 
    case _ => 
     val x = xs.head 
     val (left, right) = xs.tail.partition(p(_, x)) 
     val left_sorted = quickSort(left)(p) 
     val right_sorted = quickSort(right)(p) 
     left_sorted ::: (x :: right_sorted) 
} 

但我不知道如何把它变成尾recurisive。任何人都可以给我一个建议吗?

+5

你不能 - 算法的思想是在较小的任务中打破工作,递归执行它们,然后将结果放在一起。因为后者是nessecarily在快速排序中最后一个thibg,它不能是尾递归。 – Ingo 2012-02-12 08:45:16

+1

这个类似的问题,但在OCaml可能会帮助:http://stackoverflow.com/questions/5634083/implementing-a-tail-recursive-version-of-quick-sort-in-f-ocaml – perelman 2012-02-12 08:47:00

+0

对不起,我不不知道OCaml – 2012-02-12 08:47:41

回答

13

任何递归函数都可以转换为使用堆而不是堆栈来跟踪上下文。该过程被称为trampolining

以下是Scalaz如何实现它。

object TrampolineUsage extends App { 

    import scalaz._, Scalaz._, Free._ 

    def quickSort[T: Order](xs: List[T]): Trampoline[List[T]] = { 
    assert(Thread.currentThread().getStackTrace.count(_.getMethodName == "quickSort") == 1) 
    xs match { 
     case Nil => 
     return_ { 
      Nil 
     } 
     case x :: tail => 
     val (left, right) = tail.partition(_ < x) 
     suspend { 
      for { 
      ls <- quickSort(left) 
      rs <- quickSort(right) 
      } yield ls ::: (x :: rs) 
     } 
    } 
    } 

    val xs = List.fill(32)(util.Random.nextInt()) 
    val sorted = quickSort(xs).run 
    println(sorted) 

    val (steps, sorted1) = quickSort(xs).foldRun(0)((i, f) => (i + 1, f())) 
    println("sort took %d steps".format(steps)) 
} 

当然,你需要使用一个非常大的结构或非常小的堆栈与非尾递归鸿沟实际问题和征服算法,因为你可以处理2种堆栈^ N个元素N的深度

http://blog.richdougherty.com/2009/04/tail-calls-tailrec-and-trampolines.html

UPDATE

scalaz.Trampoline是(多)更一般的结构的一种特殊情况,Free 。它被定义为type Trampoline[+A] = Free[Function0, A]。实际上可以更一般地编写quickSort,所以它通过Free中使用的类型构造函数进行参数化。这个例子展示了这是如何完成的,以及如何使用相同的代码来使用堆栈,堆或者同时进行绑定。

https://github.com/scalaz/scalaz/blob/scalaz-seven/example/src/main/scala/scalaz/example/TrampolineUsage.scala

+0

'暂停'从哪里来? git“蹦床”上的源代码在哪里?我无法找到一个'Trampoline.scala'文件。 – huynhjl 2012-02-12 17:00:05

+0

https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/Free.scala – retronym 2012-02-12 17:05:17

+2

天真的快速排序不保证分而治之。例如,OP的算法在“List.range(0,10000).reverse”中断。 – 2012-02-12 17:49:57

7

尾递归要求传递工作,已完成和工作待办事项,在前进的每一步。所以你只需要在工作堆上封装你的工作而不是堆栈。你可以使用一个列表作为一个堆栈,这很简单。下面是一个实现:

def quicksort[T](xs: List[T])(lt: (T,T) => Boolean) = { 
    @annotation.tailrec 
    def qsort(todo: List[List[T]], done: List[T]): List[T] = todo match { 
    case Nil => done 
    case xs :: rest => xs match { 
     case Nil => qsort(rest, done) 
     case x :: xrest => 
     val (ls, rs) = (xrest partition(lt(x,_))) 
     if (ls.isEmpty) { 
      if (rs.isEmpty) qsort(rest, x :: done) 
      else qsort(rs :: rest, x :: done) 
     } 
     else qsort(ls :: List(x) :: rs :: rest, done) 
    } 
    } 
    qsort(List(xs),Nil) 
} 

这是当然的,因为通过返璞词链接到蹦床的一种特殊情况下(你不需要直传功能)。幸运的是,这种情况很容易手动完成。

+0

您的解决方案接缝不正确 – 2012-09-28 16:06:29

4

我写这篇文章包含分步说明如何将经典的实现快速排序的转换为尾递归形式:

Quicksort rewritten in tail-recursive form - An example in Scala

我希望你觉得它很有趣!

+0

您必须披露与外部博客帖子的联系。还请在答案本身发布相关内容 - 如果有一天它不可用,链接就不好。 – 2012-09-28 19:34:18