[英]How to convert recursion to fold
根据Erik Meijer的说法,作为函数式程序员,我们都知道,应该使用fold而不是递归。 如何转换以下内容以使用折叠? 我可以看到带有return的一种方法,但在fp中也应避免返回。 谢谢!
def tryOld(string: String, original: Exception, zomOldList: List[String => Double]): Double = {
zomOldList match {
case Nil =>
throw original
case head :: tail =>
try {
head(string)
} catch {
case ex: Exception =>
tryOld(string, original, tail)
}
}
}
您可以利用foldRight
来实现这一点, foldRight
利用函数作为值:
import util.control.NonFatal
def tryOld(string: String, original: Exception, zomOldList: List[String ⇒ Double]): Double = {
val unhandled: String ⇒ Double = _ ⇒ throw original
zomOldList.foldRight(unhandled) { (f, z) ⇒
x ⇒ try { f(x) } catch { case NonFatal(_) ⇒ z(x) }
}(string)
}
请注意,我们在这里使用NonFatal
来避免捕获我们不应该捕获的异常。 您可以通过不直接使用异常来以更优雅的方式编写代码。
您不能一implement而就。 fold遍历集合的每个元素,而tryOld
有时会提前终止。 您可以利用Stream
的惰性,并通过collectFirst
和Try
实现它:
import scala.util.Try
def tryOld(string: String, original: Exception, zomOldList: List[String => Double]): Double =
zomOldList.toStream.map(x => Try(x(string))) collectFirst {
case Success(x) => x
} getOrElse (throw original)
但是您原来的递归实现更清晰,性能更高。
编辑:
如果斯卡拉有foldRight
用相同的懒惰特性Haskell的foldr
,那么这可能是在来定义foldRight
:
implicit class GiveStreamAWorkingFoldRight[A](val s: Stream[A]) extends AnyVal {
def lazyFoldRight[B](z: => B)(f: (A, () => B) => B): B =
if (s.isEmpty) z else f(s.head, () => s.tail.lazyFoldRight(z)(f))
}
def tryOld(string: String, original: Exception, zomOldList: List[String => Double]): Double =
zomOldList.toStream.lazyFoldRight(throw original) { (a, b: () => Double) =>
try {
a(string)
} catch {
case ex: Exception => b()
}
}
但是,Scala缺乏真正的尾部调用优化功能,这意味着对b
每次调用都会引入一个新的堆栈帧,从而可能导致堆栈溢出。
这是foldLeft的解决方案。 自从我首次编写了一个由tryOldString调用的泛型函数以来,这很冗长
def tryOld[In, Error, Out](
in: In,
original: Error,
zomOldList: List[In => Either[Error, Out]]
): Either[Error, Out] = {
val seed: Either[Error, Out] = Left(original)
zomOldList.foldLeft(seed) {
case (prev, f) =>
// stores first match without return
if (seed != prev) {
prev
} else {
f(in).fold(
fa =>
prev,
fb =>
Right(fb)
)
}
}
}
def tryOutString(string: String, original: Exception, zomOldList: List[String => Double]): Double = {
val zomFunctions: List[String => Either[Exception, Double]] = zomOldList.map {
f =>
s: String =>
try {
Right(f(s))
} catch {
case e: Exception =>
Left(e)
}
}
tryOld(string, original, zomFunctions).fold(
bad => throw original,
good => good
)
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.