【问题标题】:Scala - sort based on Future result predicateScala - 基于未来结果谓词排序
【发布时间】:2015-05-30 08:25:51
【问题描述】:

我有一个要排序的对象数组,其中排序谓词是异步的。 Scala 是否具有基于类型签名为(T, T) -> Future[Bool] 而不仅仅是(T, T) -> Bool 的谓词进行排序的标准或第三方库函数?

或者,有没有其他方法可以构建这段代码?我已经考虑找到列表元素的所有 2 对排列,在每一对上运行谓词并将结果存储在 Map((T, T), Bool) 或某种结构中,然后对其进行排序 - 但我怀疑会有很多执行的比较甚至比简单的排序算法还要多。

【问题讨论】:

  • 预先计算所有对的排序需要n^2 比较,而大多数排序算法在n log(n) 中运行,所以你不会想这样做。
  • 这是我的怀疑!绝对是个坏主意 (TM)。

标签: algorithm scala sorting


【解决方案1】:

如果您的谓词是异步的,您可能更喜欢获得异步结果并避免使用 Await 阻塞线程

如果你想根据未来的布尔谓词对List[(T,T)]进行排序,最简单的方法是对List[(T,T,Boolean)]进行排序

所以如果你有一个List[(T,T)] 和一个谓词(T, T) -> Future[Bool],你怎么能得到一个List[(T,T,Boolean)]?或者更确切地说是Future[List[(T,T,Boolean)]],因为您想保持异步行为。

val list: List[(T,T)] = ...
val predicate = ...
val listOfFutures: List[Future[(T,T,Boolean]] = list.map { tuple2 => 
  predicate(tuple2).map( bool => (tuple2._1, tuple2._2, bool) 
}
val futureList: Future[List[(T,T,Boolean)]] = Future.sequence(listOfFutures)
val futureSortedResult: Future[List[(T,T)]] = futureList.map { list =>
    list.sort(_._3).map(tuple3 => (tuple3._1,tuple3._2))
}

这是伪代码,我没有编译它,它可能没有,但你明白了。

键是Future.sequence,非常有用,它允许将Monad1[Monad2[X]] 转换为Monad2[Monad1[X]],但请注意,如果您的任何谓词future 失败,全局排序操作也将失败。


如果您想要更好的性能,“批处理”对返回 Future[Boolean] 的服务的调用可能是一个更好的解决方案。 例如,代替(T, T) -> Future[Bool],也许您可​​以设计一个像List[(T, T)] -> Future[List[(T,T,Bool)] 这样的服务(如果您显然拥有它),这样您就可以在异步单个调用中获得所需的一切。

【讨论】:

    【解决方案2】:

    一个不太令人满意的替代方案是阻止每次比较,直到评估未来。如果评估排序谓词的成本很高,那么排序将花费很长时间。实际上,这只是将可能并发的程序转换为顺序程序;使用期货的所有好处都将丧失。

    import scala.concurrent.duration._
    implicit val executionContext = ExecutionContext.Implicits.global
    
    val sortingPredicate: (Int, Int) => Future[Boolean] = (a, b) => Future{
      Thread.sleep(20) // Assume this is a costly comparison
      a < b
    }
    
    val unsorted = List(4, 2, 1, 5, 7, 3, 6, 8, 3, 12, 1, 3, 2, 1)
    val sorted = unsorted.sortWith((a, b) =>
      Await.result(sortingPredicate(a, b), 5000.millis) // careful: May throw an exception
    )
    println(sorted) // List(1, 1, 1, 2, 2, 3, 3, 3, 4, 5, 6, 7, 8, 12)
    

    我不知道是否有使用异步比较的开箱即用解决方案。但是,您可以尝试实现自己的排序算法。如果我们考虑Quicksort,它平均运行在O(n log(n)),那么我们实际上可以很容易地利用异步比较。

    如果你不熟悉快速排序,算法基本如下

    1. 从集合中选择一个元素(称为Pivot
    2. 将枢轴与所有剩余元素进行比较。创建一个包含小于枢轴的元素和一个包含大于枢轴的元素的集合。
    3. 对两个新集合进行排序并将它们连接起来,将枢轴放在中间。

    由于第 2 步执行了大量独立比较,我们可以同时评估这些比较。

    这是一个未优化的实现:

    object ParallelSort {
      val timeout = Duration.Inf
    
      implicit class QuickSort[U](elements: Seq[U]) {
        private def choosePivot: (U, Seq[U]) = elements.head -> elements.tail
    
        def sortParallelWith(predicate: (U, U) => Future[Boolean]): Seq[U] =
          if (elements.isEmpty || elements.size == 1) elements
          else if (elements.size == 2) {
            if (Await.result(predicate(elements.head, elements.tail.head), timeout)) elements else elements.reverse
          }
          else {
            val (pivot, other) = choosePivot
            val ordering: Seq[(Future[Boolean], U)] = other map { element => predicate(element, pivot) -> element }
    
            // This is where we utilize asynchronous evaluation of the sorting predicate
            val (left, right) = ordering.partition { case (lessThanPivot, _) => Await.result(lessThanPivot, timeout) }
    
            val leftSorted = left.map(_._2).sortParallelWith(predicate)
            val rightSorted = right.map(_._2).sortParallelWith(predicate)
            leftSorted ++ (pivot +: rightSorted)
          }
      }
    
    }
    

    可以使用(与上面相同的例子)如下:

    import ParallelSort.QuickSort
    val sorted2 = unsorted.sortParallelWith(sortingPredicate)
    println(sorted2) // List(1, 1, 1, 2, 2, 3, 3, 3, 4, 5, 6, 7, 8, 12)
    

    请注意,这种快速排序的实现是否比完全顺序的内置排序算法更快或更慢在很大程度上取决于比较的成本:比较必须阻塞的时间越长,上述替代解决方案就越差.在我的机器上,考虑到代价高昂的比较(20 毫秒)和上面的列表,内置排序算法运行时间约为 1200 毫秒,而此自定义快速排序运行时间约为 200 毫秒。如果你担心性能,你可能想想出更聪明的东西。 编辑:我刚刚检查了内置排序算法和自定义快速排序算法执行了多少比较:显然,对于给定列表(以及我随机输入的一些其他列表)内置算法使用更多的比较,因此并行执行带来的性能改进可能不是那么好。我不知道更大的列表,但无论如何您都必须根据您的特定数据对它们进行分析。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-07-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-08
      • 1970-01-01
      • 2016-04-02
      相关资源
      最近更新 更多