如何使我的排序函数尾递归?
How to make my sorting function tail-recursive?
这些年来,我一直在以命令式风格编写代码,但现在开始学习函数式风格,并在将函数转换为尾递归时面临一些障碍。
尝试修改和添加打印以理解代码,但没有任何成果
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(...)))) ,其中您首先执行最内层的 f。这与尾递归相反。
您要做的是展开一层,创建结果并使用列表的其余部分进行递归。
从左到右执行此操作,您的 sortHelper 函数可能应该采用列表的当前工作版本,加上排序后的版本作为另一个参数。然后,您可以递归地从工作列表中拉出一个元素并将其放入排序列表中。如果你 运行 遇到当前元素小于排序列表头部的情况,你应该退后一步并将排序后的头部放回工作列表,然后将头部也放回列表中(因为你不知道你递归的时候会不会运行再次陷入这种情况)。
这是答案的冗长版本,让您有机会自己尝试代码。
我已经包含了下面的代码,如果你想自己试试就不要看:
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))
}
这些年来,我一直在以命令式风格编写代码,但现在开始学习函数式风格,并在将函数转换为尾递归时面临一些障碍。
尝试修改和添加打印以理解代码,但没有任何成果
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(...)))) ,其中您首先执行最内层的 f。这与尾递归相反。
您要做的是展开一层,创建结果并使用列表的其余部分进行递归。
从左到右执行此操作,您的 sortHelper 函数可能应该采用列表的当前工作版本,加上排序后的版本作为另一个参数。然后,您可以递归地从工作列表中拉出一个元素并将其放入排序列表中。如果你 运行 遇到当前元素小于排序列表头部的情况,你应该退后一步并将排序后的头部放回工作列表,然后将头部也放回列表中(因为你不知道你递归的时候会不会运行再次陷入这种情况)。
这是答案的冗长版本,让您有机会自己尝试代码。
我已经包含了下面的代码,如果你想自己试试就不要看:
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))
}