2014-10-18 3 views
3

У меня есть некоторые побочные effectful функцию,Есть ли последовательный Future.find?

def f(): Future[Int] = { 
    val n = Random.nextInt() 
    println(s"Generated $n") 
    Future(n) 
} 

, и я хочу, чтобы выполнить его до тех пор, пока предикат возвращает истину.

def success(n: Int): Boolean = n % 2 == 0 

Мой план заключается в создании Stream результатов

val s = Stream.fill(10)(f) 

, а затем использовать Future.find, чтобы получить первый результат, который удовлетворяет предикату.

Future.find(s)(success) map println 

Проблема заключается в том, что Future.find запускает все фьючерсы параллельно, и я хочу, чтобы выполнить фьючерсы последовательно один за другим до тех пор, пока предикат возвращает истину.

scala> Future.find(s)(success) map println 
Generated -237492703 
Generated -935476293 
Generated -1155819556 
Generated -375506595 
Generated -912504491 
Generated -1307379057 
Generated -1522265611 
Generated 1163971151 
Generated -516152076 
res8: scala.concurrent.Future[Unit] = [email protected] 
Some(-1155819556) 

Вопрос заключается в том, как последовательно выполнять поток фьючерсов, пока предикат не вернет true? Существуют ли подходящие функции в стандартной или сторонней библиотеке?

+0

Возможно, вы посмотрите на 'scalaz-stream' или' iteratee' – jilen

+0

Дубликат для случая std lib. http://stackoverflow.com/questions/26349318/how-to-invoke-a-method-again-and-again-until-it-returns-a-future-value-contain –

ответ

1

Во-первых, давайте создадим будущее, мы не заинтересованы в провал:

val s1 = s.map(_.filter(success)) 

Теперь вы можете объединить два таких фьючерсов и получить первое успешное значение с помощью fallbackTo. И только раз потока, начиная с известным-плохим будущим:

def firstSuccess[T](stream: Stream[Future[T]]): Future[T] = 
    if (stream.isEmpty) 
    Future.failed(new NoSuchElementException) 
    else 
    stream.head.fallbackTo(firstSuccess(stream.tail)) 
+0

'foldLeft' выполняет все фьючерсы, но Я хочу выполнить достаточно фьючерсов, чтобы удовлетворить предикат. – lambdas

+0

@lambdas Ой. Конечно, вы правы, вместо этого вам нужна ленивая правая смена. Ред. –

+0

И снова отредактирован, так как шаблон, соответствующий потоку, не достаточно ленив. –

0

Если я правильно понял вопрос, то вам придется блокировать поток, чтобы продолжить последовательно. Вы можете использовать Await для этого.

scala> def f(): Future[Int] = { 
| val n = Random.nextInt() 
| println(s"Generated $n") 
| Future(n) 
| } 
f:()scala.concurrent.Future[Int] 

scala> def success(n: Int): Boolean = n % 2 == 0 
success: (n: Int)Boolean 

scala> val s = Stream.fill(10)(f) 

Используя свой путь, я получаю

scala> Future.find(s)(success) map println 
Generated 551866055 
Generated -561348666 
Generated -1103407834 
Generated -812310371 
Generated -1544170923 
Generated 2131361419 
Generated -236722325 
Generated -1473890302 
Generated -82395856 
Some(-561348666) 
res16: scala.concurrent.Future[Unit] = [email protected] 

я должен получить ответ, как некоторые (-561348666), который вы можете получить, как

scala> s.find(x => success(Await.result(x,1 seconds))).get onSuccess {case p=> println(p)} 
-561348666 
+0

Я хотел бы получить Future [Option [Int]], нормально ли обернуть ваше последнее выражение в 'Future {...}'? 'Future' с' Await' внутри, выглядит странно;) – lambdas

+0

Я не знаю, как вы собираетесь его использовать. Вы можете обернуть выражение, но оно может быть или не быть правильным в зависимости от вашего варианта использования. s.find (x => success (Await.result (x, 1 секунда))). get return Будущее [Int] – mohit

4

Вместо использования потока я предлагаю использовать другой подход. Использование фильтра в будущем и recoverWith рекурсивно:

def findFirst[A](futureGen: => Future[A], predicate: A => Boolean): Future[A] = { 
    futureGen.filter(predicate).recoverWith { case _ => findFirst(futureGen, predicate) } 
} 

findFirst(f, success) 

Это будет вызывать Фьючерсных один за другим, пока «успех» возвращает истину.

+0

Похоже, что рекурсия с ручным управлением я использую, за исключением 'filter' и' recoverWith':) – lambdas

+0

Ну ... Иногда вы должны придерживаться простых решений. – roterl