Scala Параллельные коллекции. Как вернуться раньше?
У меня есть список возможных значений ввода
val inputValues = List(1,2,3,4,5)
У меня есть очень длинная функция вычисления, которая дает мне результат
def reallyLongFunction( input: Int ) : Option[String] = { ..... }
Используя scala параллельные коллекции, я легко могу сделать
inputValues.par.map( reallyLongFunction( _ ) )
Чтобы получить все результаты, параллельно. Проблема в том, что я действительно не хочу всех результатов, мне нужен только результат FIRST. Как только один из моих вкладов будет успешным, я хочу получить свой результат и хочу продолжить свою жизнь. Это сделало много дополнительной работы.
Итак, как мне получить лучшее из обоих миров? Я хочу
- Получить первый результат, который возвращает что-то из моей длинной функции
- Остановите все мои другие потоки от бесполезной работы.
Изменить -
Я решил это как немой программист java, имея
@volatile var done = false;
который установлен и проверен внутри моего reallyLongFunction
. Это работает, но не очень сильно scala. Хотелось бы лучше сделать это....
Ответы
Ответ 1
Я воспринял ваш вопрос так же, как huynhjl, но если вы просто хотите искать и отбрасывать None
s, вы можете сделать что-то подобное, чтобы избежать необходимости повторять вычисление, когда найден подходящий результат:
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 по крайней мере.
Летучие флаги используются в параллельных коллекциях (посмотрите на источник для find
, exists
и forall
), поэтому я думаю, что ваша идея хорошая. Это действительно лучше, если вы можете включить флаг в самой функции. Он убивает ссылочную прозрачность вашей функции (т.е. Для некоторых входов ваша функция теперь иногда возвращает None
, а не Some
), но поскольку вы отбрасываете остановленные вычисления, это не имеет значения.
Ответ 2
(Обновлено: нет, он не работает, не делает карту)
Будет ли работать что-то вроде:
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, за исключением того, что вам не нужно его смотреть; -)
Ответ 3
Если вы хотите использовать неосновную библиотеку, я думаю, что Futures будет хорошим выбором для этой задачи. Например:
... оба из которых, как представляется, позволяют использовать функциональность, которую вы ищете.