来自finally块的Scala尾部递归



我想将函数f应用于List的每个元素,而不是在第一个错误处停止,而是只抛出最后一个错误(如果有的话):

@annotation.tailrec
def tryAll[A](xs: List[A])(f: A => Unit): Unit = {
xs match {
case x :: xt =>
try {
f(x)
} finally {
tryAll(xt)(f)
}
case _ =>
}
}

但是,上面的代码没有编译——它抱怨这个函数不是尾部递归的。为什么不呢?

此解决方案对所有元素进行迭代,并产生(抛出)最后一个错误(如果有的话):

def tryAll[A](xs: List[A])(f: A => Unit): Unit = {
val res = xs.foldLeft(Option.empty[Throwable]) {
case (maybeThrowable, a) =>
Try(f(a)) match {
case Success(_) => maybeThrowable
case Failure(e) => Option(e)
}
}
res.foreach(throwable => throw throwable)
}

正如@HristoIliev所提到的,您的方法不能是尾部递归的,因为finally调用不能保证是尾部调用。这意味着以这种方式使用try的任何方法都不会是尾部递归的。另请参阅此答案。

再次调用该方法是一种奇怪的重复尝试直到成功的方式,因为在每个阶段,它都会抛出一个您可能没有处理的异常。相反,我主张使用Try的函数方法,从一个角度看待失败,直到操作成功。这种方法唯一的缺点是,它不会抛出任何异常供您处理(这也是一个优势!)。

def tryAll[A](xs: List[A])(f: A => Unit): Unit =
xs.view.map(x => Try(f(x))).takeWhile(_.isFailure).force

scala> val list = List(0, 0, 0, 4, 5, 0)
scala> tryAll(list)(a => println(10 / a))
2

如果您真的想处理异常(或只处理最后一个异常),您可以将返回类型tryAll更改为List[Try[Unit]](如果您修改代码以只处理最后的一个,则可以简单地将返回类型改为Try[Unit])。方法的返回类型最好描述它实际执行的部分操作——可能返回错误。

不确定该方法的意图,但您可以这样做:

final def tryAll[A](xs: List[A])(f: A => Unit): Unit = {
xs match {
case x :: xt =>
try {
f(x)
} catch {
case e => tryAll(xt)(f)
}
case _ => //do something else
}
}

我知道使用@annotation.tailrec 的方法

由此:

def fac(n:Int):Int = if (n<=1) 1 else n*fac(n-1)

你应该有这个:

@scala.annotation.tailrec 
def facIter(f:Int, n:Int):Int = if (n<2) f else facIter(n*f, n-1) 
def fac(n:Int) = facIter(1,n)

相关内容

  • 没有找到相关文章

最新更新