14

我有一个可能的输入值列表

val inputValues = List(1,2,3,4,5)

我有一个很长的计算函数,它给了我一个结果

def reallyLongFunction( input: Int ) : Option[String] = { ..... }

使用 scala 并行集合,我可以轻松做到

inputValues.par.map( reallyLongFunction( _ ) )

并行获取所有结果。问题是,我真的不想要所有的结果,我只想要第一个结果。一旦我的一个输入成功,我想要我的输出,并且想要继续我的生活。这做了很多额外的工作。

那么如何才能两全其美呢?我想要

  1. 获取从我的长函数返回的第一个结果
  2. 停止我所有其他线程的无用工作。

编辑- 我像一个愚蠢的Java程序员一样解决了它

@volatile var done = false;

这是在我的reallyLongFunction. 这有效,但感觉不是很scala。想要一个更好的方法来做到这一点......

4

3 回答 3

4

(更新:不,它不起作用,不做地图)

做类似的事情会起作用吗:

inputValues.par.find({ v => reallyLongFunction(v); true })

实现使用这个:

  protected[this] class Find[U >: T](pred: T => Boolean, protected[this] val pit: IterableSplitter[T]) extends Accessor[Option[U], Find[U]] {
    @volatile var result: Option[U] = None
    def leaf(prev: Option[Option[U]]) = { if (!pit.isAborted) result = pit.find(pred); if (result != None) pit.abort }
    protected[this] def newSubtask(p: IterableSplitter[T]) = new Find(pred, p)
    override def merge(that: Find[U]) = if (this.result == None) result = that.result
  }

它在精神上看起来与您的 @volatile 非常相似,只是您不必看它;-)

于 2011-12-11T23:35:05.373 回答
3

我以与 huynhjl 相同的方式解释了您的问题,但如果您只想搜索并丢弃Nones,您可以执行以下操作以避免在找到合适的结果时重复计算:

class Computation[A,B](value: A, function: A => B) {
  lazy val result = function(value)
}

def f(x: Int) = {          // your function here
  Thread.sleep(100 - x)
  if (x > 5) Some(x * 10)
  else None
}

val list = List.range(1, 20) map (i => new Computation(i, f))  
val found = list.par find (_.result.isDefined) 
  //found is Option[Computation[Int,Option[Int]]]
val result = found map (_.result.get)
  //result is Option[Int]

然而find,对于并行集合似乎做了很多不必要的工作(参见这个问题),所以这可能效果不佳,至少在当前版本的 Scala 中是这样。

可变标志用于并行集合(查看 、 和 的来源findexistsforall所以我认为您的想法是一个好主意。如果您可以在函数本身中包含标志,实际上会更好。它会破坏函数的引用透明度(即,对于某些输入,您的函数现在有时会返回None而不是Some),但是由于您要丢弃停止的计算,因此这无关紧要。

于 2011-12-12T16:47:25.033 回答
2

如果您愿意使用非核心库,我认为 Futures 将非常适合这项任务。例如:

...两者似乎都可以启用您正在寻找的功能。

于 2011-12-12T17:09:54.453 回答