Обработка последовательности с дубликатами одновременно - PullRequest
2 голосов
/ 16 июня 2019

Предположим, у меня есть функция fab: A => B, последовательность A и мне нужно получить последовательность пар (A, B), например:

def foo(fab: A => B, as: Seq[A]): Seq[(A, B)] = as.zip(as.map(fab))

Теперь я хочу запустить fab одновременно используя scala.concurrent.Future, но я хочу запустить fab только один раз для всех дублирующих элементов в as.Например,

val fab: A => B = ...
val a1: A = ...
val a2: A = ...
val as = a1 :: a1 :: a2 :: a1 :: a2 :: Nil
foo(fab, as) // invokes fab twice and run these invocations concurrently

Как бы вы это реализовали?

1 Ответ

5 голосов
/ 16 июня 2019
def foo[A, B](as: Seq[A])(f: A => B)(implicit exc: ExecutionContext)
: Future[Seq[(A, B)]] = {
  Future
    .traverse(as.toSet)(a => Future((a, (a, f(a)))))
    .map(abs => as map abs.toMap)
}

Пояснение:

  1. as.toSet гарантирует, что f вызывается только один раз для каждого a
  2. (a, (a, f(a))) дает вам набор с вложенными кортежами формы (a, (a, b))
  3. Отображение исходной последовательности a с помощью Map с парами (a, (a, b)) дает последовательность (a, b) с.

Поскольку ваш f в любом случае не является асинхронным, и поскольку вы не возражаете против использования фьючерсов, вы также можете рассмотреть возможность использования par -коллекций:

def foo2[A, B](as: Seq[A])(f: A => B): Seq[(A, B)] = {
  as map as.toSet.par.map((a: A) => a -> (a, f(a))).seq.toMap
}
...