以下函数在嵌套列表中过滤(删除1
')
def filter(list : List[Any], acc : List[Any] = Nil) : List[Any] = {
list match {
case Nil => acc
case (l : List[_]) :: tail =>
val nested = filter(l)
if (nested.isEmpty) filter(tail, acc)
else
filter(tail, acc :+ nested)
case 1 :: tail => filter(tail, acc)
case other :: tail => filter(tail, acc :+ other)
}
}
输入
filter(List(1, 3, 4, List(1, 4 ,5), List(1)))
输出
res0: List[Any] = List(3, 4, List(4, 5))
我的问题是:如何使这个尾递归?我的主要问题是如何处理嵌套列表:val nested = filter(l, Nil)
由于
答案 0 :(得分:2)
好的,您可以使用免费monad 来解决此问题,例如,在Typelevel Cats lib中可用。
"org.typelevel" %% "cats-free" % "1.0.1"
背后的想法是将递归无法转换为尾递归到另一部分代码,这是可行的
因此,如果您想要了解如何解决此类任务,您需要阅读Runar Oli Bjarnason的this论文(并且可能会看this演示文稿)。之后,您可以尝试使用自己编写的蹦床解决问题(基于纸张和视频):
首先让我们以某种方式重写你的过滤器(这一步不是必需的,我只是在纸上的偶数/奇数例子的印象中,下面我展示了如何根据你的变体做同样的事情):
private def filterList(l: List[Any]): Option[Any] = {
l.foldLeft(Option.empty[List[Any]]) {
case (acc, next) if acc.isEmpty =>
filter1(next).map(_ :: Nil)
case (acc, next) =>
acc.map(_ ++ filter1(next))
}
}
private def filter1(a: Any): Option[Any] = a match {
case 1 => Option.empty[Any]
case l: List[Any] => filterList(l)
case t => Some(t)
}
def filter(l: List[Any]): List[Any] = {
filterList(l) match {
case Some(l: List[Any]) => l
case _ => Nil
}
}
现在让我们假设Trampoline
已经可用(并且可以使用,见下文)并使用trampolines重写:
def filterList(l: List[Any]): Trampoline[Option[Any]] = {
l.foldLeft[Trampoline[Option[List[Any]]]](Done(Option.empty[List[Any]])) {
case (acc, next) =>
acc.flatMap {
case None => filter1(next).map(_.map(_ :: Nil))
case v =>
filter1(next).map (r => v.map(_ ++ r))
}
}
}
def filter1(a: Any): Trampoline[Option[Any]] = a match {
case 1 => Done(Option.empty[Any])
case l: List[Any] => More(() => filterList(l))
case t => Done(Some(t))
}
def filter(l: List[Any]): List[Any] = {
filterList(l).runT match {
case Some(l: List[Any]) => l
case _ => Nil
}
}
Trampoline
实施(基于纸张和视频)
sealed trait Trampoline[+A] {
final def runT: A =
this match {
case Done(a) => a
case More(k) => k().runT
case t: FlatMap[Any, A] => t match {
case Done(a) FlatMap f => f(a).runT
case More(k) FlatMap f => k().flatMap(f).runT
case FlatMap(xg: FlatMap[Any, A], f) =>
xg.a.flatMap(a => xg.f(a)).runT
}
}
def map[B](f: A => B): Trampoline[B] =
flatMap(x => More(() => Done(f(x))))
def flatMap[B](f: A => Trampoline[B]): Trampoline[B] = this match {
case FlatMap(a, g: Any) => FlatMap(a, (x: Any) => g(x) flatMap f)
case x => FlatMap(x, f)
}
}
case class More[+A](k: () => Trampoline[A])
extends Trampoline[A]
case class Done[+A](result: A)
extends Trampoline[A]
case class FlatMap[A, B](a: Trampoline[A], f: A => Trampoline[B])
extends Trampoline[B]
现在,通过所有这些部分,您可以检查现在您是否正在进行无堆栈递归实现。我用这样的代码进行测试:
def check(i: Int, output: Boolean) {
val l = (1 to i).foldLeft(List[Any](1, 2, 3)) {
case (l, _) =>
List[Any](1, 2, 3, l, List(1))
}
val res = filter(l)
if (output) {
println(s"Depth: $i, result: " + res)
}
}
check(3, true)
check(10000, false)
使用StackOverflowError
的非蹦床方法失败,并在修改过的方法上成功完成。
使用Cats更新
我尝试使用cats-free
lib的相同方法。它也很完美:
import cats.free._
import cats.implicits._
def filterList(l: List[Any]): Trampoline[Option[Any]] = {
l.foldLeft[Trampoline[Option[List[Any]]]](Trampoline.done(Option.empty[List[Any]])) {
case (acc, next) =>
acc.flatMap {
case None => filter1(next).map(_.map(_ :: Nil))
case v =>
filter1(next).map (r => v.map(_ ++ r))
}
}.map(_.map(identity[Any]))
}
def filter1(a: Any): Trampoline[Option[Any]] = a match {
case 1 => Trampoline.done(Option.empty[Any])
case l: List[Any] => Trampoline.defer(filterList(l))
case t => Trampoline.done(Some(t))
}
def filter(l: List[Any]): List[Any] = {
filterList(l).run match {
case Some(l: List[Any]) => l
case _ => Nil
}
}
使用Cats Trampoline和原始代码
我尝试对原始代码执行相同操作,结果如下:
import cats.free._
import cats.implicits._
private def filterInner(list : List[Any], acc : List[Any] = Nil) : Trampoline[List[Any]] = {
list match {
case Nil => Trampoline.done(acc)
case (l : List[_]) :: tail =>
Trampoline.defer(filterInner(l)).flatMap {
case Nil => Trampoline.defer(filterInner(tail, acc))
case nested => Trampoline.defer(filterInner(tail, acc :+ nested))
}
case 1 :: tail => Trampoline.defer(filterInner(tail, acc))
case other :: tail => Trampoline.defer(filterInner(tail, acc :+ other))
}
}
def filter(list : List[Any]): List[Any] = filterInner(list).run
注意,对filterInner
的每次递归调用都包含在Trampoline.defer
中,这样做是为了消除递归。您可以通过删除case (l : List[_]) :: tail
部分中的包装并运行我的测试示例来测试它。