Scala Parallel Collections - Как вернуться рано? - PullRequest
14 голосов
/ 12 декабря 2011

У меня есть список возможных значений ввода

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. Это работает, но не очень скала. Хотел бы лучший способ сделать это ...

Ответы [ 3 ]

4 голосов
/ 12 декабря 2011

(Обновлено: нет, карта не работает, карта не работает)

Будет ли работать что-то вроде:

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 голосов
/ 12 декабря 2011

Я интерпретировал ваш вопрос так же, как 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 голосов
/ 12 декабря 2011

Если вы хотите использовать неосновную библиотеку, я думаю, что Futures подойдет для этой задачи. Например:

... оба из которых, по-видимому, активируют нужную вам функцию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...