Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 未来递归模式/任意长度的未来链接_Scala_Recursion_Akka_Future - Fatal编程技术网

Scala 未来递归模式/任意长度的未来链接

Scala 未来递归模式/任意长度的未来链接,scala,recursion,akka,future,Scala,Recursion,Akka,Future,我很好奇递归构建Akka futures链的最佳方法,该链将按顺序运行,如果未来的doWork调用失败,未来最多应重试3次,如果重试次数不足,该链应失败。假设所有doWork调用都通过了返回的futurefutChain应该只完成 object Main extends App { val futChain = recurse(2) def recurse(param: Int, retries: Int = 3): Future[String] { Future {

我很好奇递归构建Akka futures链的最佳方法,该链将按顺序运行,如果未来的
doWork
调用失败,未来最多应重试3次,如果重试次数不足,该链应失败。假设所有
doWork
调用都通过了返回的future
futChain
应该只完成

object Main extends App {
  val futChain = recurse(2)

  def recurse(param: Int, retries: Int = 3): Future[String] {
    Future {
      doWorkThatMayFailReturningString(param...)
    } recoverWith {
      case e => 
        if (retries > 0) recurse(param, retries -1)
        else  Future.failed(e)
    } flatMap {
      strRes => recurse(nextParam) //how should the res from the previous fut be passed?
    }
  }

  futChain onComplete {
    case res => println(res) //should print all the strings
  }
}
  • 如何将结果作为集合获取?i、 e.在本例中,每个
    String
    doWork
    函数返回(我需要以某种方式修改
    recurse
    func以返回
    Futrue[List[String]
  • 我应该使用
    recover
    还是
    recoverWith
  • 调用
    flatMap
    链接这些调用可以吗
  • 我应该考虑尾部递归和堆栈溢出吗
  • 我是否最好递归地建立一个未来列表并减少它们

  • 您可以实现如下可重试的
    未来

    def retry[T](f: => Future[T])(n: Int)(implicit e: ExecutionContext): Future[T] = {
        n match {
            case i if (i > 1) => f.recoverWith{ case t: Throwable => retry(f)(n - 1)}
            case _ => f
        }       
    }
    
    这并不是针对尾部递归进行优化的,但是如果您只想重试几次,就不会出现堆栈溢出(我想,如果前几次失败了,那么无论如何它都会继续失败)

    然后我将分别进行链接。如果要链接在一起的函数数量有限,则每个函数都取决于前面的函数(出于某种原因,您希望聚合结果),您可以使用
    进行理解(语法糖用于
    flatMap
    ):

    这将解开从
    List[Future[String]]
    Future[List[String]]
    的问题

    下面是一种按顺序执行类似操作的方法:

    def sequential[A, B](seq: List[A])(f: A => Future[B])(implicit e: ExecutionContext): Future[List[B]] = {
        seq.foldLeft(Future.successful(List[B]())) { case (left, next) =>
            left.flatMap(list => f(next).map(_ :: list))
        }
    }
    
    def doWork(param: String): String = ...
    
    val results: Future[List[String]] = sequential(parameters)(param => Future(doWork(param))) 
    
    这些函数的实现对您的用例非常敏感。如果链中的任何一个期货失败,上述两个函数将返回失败的期货。有时您会希望这样,有时则不会。如果您只希望收集成功的期货,并放弃失败的期货而不使整个结果失败,则可以添加额外的step以恢复故障


    此外,
    recover
    recoverWith
    之间的区别在于它接受的
    PartialFunction
    的类型。
    recover
    用默认值替换失败的期货,而
    recoverWith
    则使用另一个
    Future
    进行替换。在我的
    重试
    的情况下,
    recoverWith
    是的,我理解重试语义。问题实际上是关于如何实现任意链。您能否提供如何实现arb链的示例。使用上面的理解方法与recursion@NightWolf你应该做些什么您的问题更清楚。将重试和将来的链接实现分开也很好。感谢您的更新!这是一个很好的方法,与此类似,但问题是这不是真正的递归,因为我需要在开始时定义参数集。为什么它需要递归?我的上一个示例使用预定义ed参数集,所以我不太确定您在寻找什么。这个问题归结为如何使用递归创建任意长的未来链。不仅仅是在一个用于理解的
    foldLeft
    中插入一堆预定义的未来,还可以用于创建动态大小的
    future
    @RandallSchulz的链。您愿意吗我想举个例子吗?是的,但是我必须等到星期一回到办公室。
    def doWork(param: String): String = ...
    
    val parameters: List[String] = List(...)
    
    val results: Future[List[String]] = Future.sequence(parameters.map(doWork(_)))
    
    def sequential[A, B](seq: List[A])(f: A => Future[B])(implicit e: ExecutionContext): Future[List[B]] = {
        seq.foldLeft(Future.successful(List[B]())) { case (left, next) =>
            left.flatMap(list => f(next).map(_ :: list))
        }
    }
    
    def doWork(param: String): String = ...
    
    val results: Future[List[String]] = sequential(parameters)(param => Future(doWork(param)))