在scala期货列表上实现真正的“forall”

时间:2018-02-27 11:56:25

标签: scala future divide-and-conquer

我正在使用scala(2.12)期货对复杂问题采用并行分而治之的方法。这是一些(简化的)上下文:

def solve(list: List[Int], constraints: Con): Future[Boolean] =
  Future.unit.flatMap{ _ =>
    //positive case
    if(list.isEmpty) Future.successful(true)

    //negative case
    else if(someTest(constraints)) Future.successful(false)

    //divide and conquer
    else {
      //split to independent problems, according to constraints
      val components: List[List[Int]] = split(list,constraints)

      //update the constraints accordingly (heavy computation here)
      val newConstr: Con = updateConstr(...)

      val futureList = components.map(c => solve(c,newConstr))
      allTrue(Future.successful(true), futureList)
    }
  }

此递归函数采用整数变量列表和表示问题约束的Con对象,并在每次调用期间产生多个独立的子问题。

我的问题的相关部分是对allTrue的调用。如果我按顺序解决问题,我会写components.forall(c => solve(c,newConstr))。然而,在并发版本中,我有类似的东西 这,它不会在遇到的第一个false情况下停止计算。

//async continuation passing style "forall"
def allTrue(acc: Future[Boolean], remaining: List[Future[Boolean]]): 
  Future[Boolean] = {
    remaining match {
      case Nil => acc
      case r :: tail => acc.flatMap{ b => 
        if(b) allTrue(r,tail)
        else{
          //here, it would be more efficient to stop all other Futures
          Future.successful(false)
        }
      }
    }
  }

我已经阅读了多篇博客文章和论坛帖子,讨论如何阻止scala期货通常不是一个好主意,但在这种情况下我认为它会非常有用。

关于如何在期货清单上获得forall行为的任何想法?

1 个答案:

答案 0 :(得分:2)

不停止期货的简单方法是Future.traverse

val all:Future[List[Boolean]] = Future.traverse(components)(c => solve(c, newConstr)
val forAll:Future[Boolean] = all.map(_.forall(identity))

对于可取消的期货清单,我建议您查看Observable模式。在您的情况下,订户可以在看到False值时取消订阅,生产者将在没有订阅者监听时停止计算