【问题标题】:Future composition in Scala with chunked response具有分块响应的 Scala 中的未来组合
【发布时间】:2015-12-12 20:38:26
【问题描述】:

我想我了解未来组合的工作原理,但我很困惑如何根据第一个未来的响应块调用下一个未来。 假设第一个未来返回一个整数列表并且列表很大。我想一次将一些函数应用于该列表,其中包含 2 个元素。我该怎么做?

这个例子总结了我的困境:

val a = Future(List(1,2,3,4,5,6))
def f(a: List[Int]) = Future(a map (_ + 2))
val res = for {
 list <- a
 chunked <- list.grouped(2).toList
} yield f(chunked)

<console>:14: error: type mismatch;
 found   : List[scala.concurrent.Future[List[Int]]]
 required: scala.concurrent.Future[?]
        chunked <- list.grouped(2).toList
            ^

返回类型必须是 Future[?] 所以我可以通过将第二个 future 移动到 yield 部分来修复它:

val res = for {
  list <- a
} yield {
  val temp = for {
    chunked <- list.grouped(2).toList
  } yield f(chunked)
  Future.sequence(temp)
}

我觉得它现在失去了它的优雅,因为它变成了嵌套的(见两个理解而不是第一种方法中的一个)。有没有更好的方法来达到同样的效果?

【问题讨论】:

    标签: scala


    【解决方案1】:

    考虑

    a.map { _.grouped(2).toList }.flatMap { Future.traverse(_)(f) }
    

    或者,如果您出于某种原因只使用for 理解,这里是如何,没有“作弊”:)

    for {
      b <- a
      c <- Future.traverse(b.grouped(2).toList)(f)
    } yield c
    

    根据评论进行编辑如果需要,向分块列表添加更多处理并不难:

    for {
       b <- a
       chunks = b.grouped(2).toList
       processedChunks = processChunks(chunks)
       c <- Future.traverse(processedChunks)
    } yield c
    

    或者,没有for理解:

    a
    .map { _.grouped(2).toList }
    .map(processChunks)
    .flatMap { Future.traverse(_)(f) }
    

    【讨论】:

    • 感谢此解决方案有效!但是我认为,扩展这个解决方案变得非常困难。就像我想进一步处理分块列表然后应用 f 它在这里很难:(
    • 嗯,你想“扩展”的东西越多,通常就越难……这就是程序员赚大钱的原因。 :) 我的意思是,做更多的处理比做更少的处理更困难,这并不奇怪,是吗?我编辑了答案以展示如何完成它的总体思路。
    【解决方案2】:

    您不能将FutureList 混为一谈。所有涉及的对象必须是同一类型。此外,在您的工作示例中,您的结果值 res 的类型为 Future[Future[List[List[Int]]]],这可能不是您想要的。

    import scala.concurrent._
    import scala.concurrent.ExecutionContext.Implicits.global
    a: scala.concurrent.Future[List[Int]] = scala.concurrent.impl.Promise$DefaultPromise@3bd3cdc8
    f: (a: List[Int])scala.concurrent.Future[List[Int]]
    
    scala> val b: Future[List[List[Int]]] = a.map(list => list.grouped(2).toList)
    b: scala.concurrent.Future[List[List[Int]]] = scala.concurrent.impl.Promise$DefaultPromise@74db196c
    
    scala> val res: Future[List[List[Int]]] = b.flatMap(lists => Future.sequence(lists.map(f)))
    res: scala.concurrent.Future[List[List[Int]]] = scala.concurrent.impl.Promise$DefaultPromise@28f9873c
    

    为了理解

    for {
        b ← a.map(list ⇒ list.grouped( 2 ).toList)
        res ← Future.sequence(b.map(f))
    } yield res
    

    【讨论】:

    • 当你说All involved objects have to be of the same type 它本质上意味着a &lt;- b 类型的操作b 返回类型必须是Future[?]。你也可以在里面使用 map 来理解它的作弊:D(因为它本质上是一种新的理解)
    • 您也可以删除map,并在c &lt;- Future.successful(b.grouped(2).toList)下方添加一个新行。
    猜你喜欢
    • 2014-04-15
    • 1970-01-01
    • 2015-05-07
    • 1970-01-01
    • 1970-01-01
    • 2021-12-07
    • 2020-06-05
    • 2020-03-16
    • 1970-01-01
    相关资源
    最近更新 更多