scala - 如何使我的排序函数尾递归?

标签 scala tail-recursion

这些年来,我一直在以命令式风格进行编码,但现在开始学习函数式风格,并面临着将函数转换为尾递归的一些障碍。

尝试修改和添加打印来理解代码,但没有任何成果

def sort(compare: (A, A) => Int): MyList[A] = {
    def sortHelper(x: A, xs: MyList[A]): MyList[A] = {
      if (xs.isEmpty) {
        new Cons(x, Empty)
      }
      else if(compare(x, xs.head) < 0)
      {
        new Cons(x, xs)
      }
      else
      {
         new Cons(xs.head, sortHelper(x, xs.tail))
      }
    }

    sortHelper(h, t.sort(compare))
  }
}

val listOfInts = new Cons(1, new Cons(2, new Cons(3, new Cons(4, Empty)
println(listOfInts.sort((x, y) => y - x))

这个列表被排序为 [4,3,2,1] 并且上面的代码可以工作,但不知道如何使其成为尾递归。

任何指导都会有所帮助。

@Nigel - 我尝试了你对我当前版本的解决方案,它有效:

def anotherSort(isLess: (A, A) => Boolean): MyList[A] = {
    @tailrec
    def sortHelper(workList: MyList[A], sortedList: MyList[A] = Empty): MyList[A] =
    if(workList.isEmpty) sortedList
    else {
      if(sortedList.isEmpty) sortHelper(workList.tail, new Cons(workList.head, Empty))
      else if(isLess(workList.head, sortedList.head)) sortHelper(new Cons(workList.head, new Cons(sortedList.head, workList.tail)), sortedList.tail)
      else sortHelper(workList.tail, new Cons(workList.head, sortedList))
    }

    sortHelper(this)
  }

感谢您澄清这一点。

最佳答案

在您的实现中,您导航->然后工作<-这类似于 Right Fold .

如果您将 List 视为调用堆栈,则您将首先执行最内部的 f(f(f(f(...)))) 。这与尾递归相反。

您想要做的是展开一层,创建结果并使用列表的其余部分进行递归。

从左到右执行此操作,您的 sortHelper 函数可能应该采用列表的当前工作版本,以及排序版本作为另一个参数。然后,您可以递归地从工作列表中提取一个元素并将其放入排序列表中。如果遇到当前元素小于已排序列表的头部的情况,则应该返回 peddle 并将已排序的头部放回到工作列表中,然后将头部也放回列表中(因为你不这样做)不知道你递归的时候会不会再次遇到这种情况)。

这是答案的冗长版本,让您有机会亲自尝试代码。

我已经包含了下面的代码,如果您想自己尝试一下,请不要看:

import scala.annotation.tailrec

sealed trait MyList[+A] extends Product with Serializable {
  val isEmpty: Boolean
  def head: A
  def tail: MyList[A]
  def sort(isLess: (A, A) => Boolean): MyList[A] = {
    @tailrec
    def inner(working: MyList[A], sorted: MyList[A] = Empty): MyList[A] = working match {
      case Empty => sorted
      case Cons(h, t) =>
        if (sorted.isEmpty) inner(t, Cons(h, Empty))
        else if (isLess(h, sorted.head)) inner(Cons(h, Cons(sorted.head, t)), sorted.tail)
        else inner(t, Cons(h, sorted))
    }

    inner(this)
  }
}

case object Empty extends MyList[Nothing] {
  val isEmpty = true
  def head = throw new NotImplementedError("No head on Empty list")
  def tail = throw new NotImplementedError("No tail on Empty list")
}

case class Cons[+A](head: A, tail: MyList[A]) extends MyList[A] {
  val isEmpty = false
}

object SortTest extends App {
  val listOfInts: MyList[Int] = Cons(1, Cons(2, Cons(3, Cons(4, Empty))))
  val isLess: (Int, Int) => Boolean = (x, y) => x < y

  println(listOfInts.sort(isLess))
}

只是为了好玩,这里调整为使用 Scala 的 List 和 Ordering 类型类:

import scala.annotation.tailrec
import scala.Ordering.Implicits._

object SortTest extends App {
  def sort[A: Ordering](lst: List[A]): List[A] = {
    @tailrec
    def inner(working: List[A], sorted: List[A] = Nil): List[A] = working match {
      case Nil => sorted
      case h :: t =>
        val (w, s) = sorted.headOption.fold((t, h :: Nil)){ sh =>
          if (h < sh) (h :: sh :: t, sorted.tail)
          else (t, h :: sorted)
        }

        inner(w, s)
    }

    inner(lst)
  }

  val listOfInts: List[Int] = 1 :: 2 :: 3 :: 4 :: Nil

  println(sort(listOfInts))
}

关于scala - 如何使我的排序函数尾递归?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58123583/

相关文章:

c++ - 迭代对数求幂

algorithm - 尾调用优化是否适用于递归调用以外的调用?

scala - 当我使用 "Reader monad"进行依赖注入(inject)时如何注入(inject)多个依赖项?

python - OverflowError : (34, 'Numerical result out of range' ) 在编写 pow(x,n) 的自定义实现时

algorithm - 递归思维的算法是什么? (在具体例子上)

Scala:自动检测 CSV 文件中的定界符/分隔符

string - 如何以递归方式思考?

scala - Play Framework : How to define a writable object in scala?

mysql - 为什么SparkSQL在访问MySQL表中的任何值时总是返回超出范围的值?

scala - 使用折叠计算 Scala 列表中的 Int 数