Подтвердить что ты не робот

Scala return on first Некоторые в списке

У меня есть список l:List[T1] и в настоящее время я делаю следующее:

myfun : T1 -> Option[T2]
val x: Option[T2] = l.map{ myfun(l) }.flatten.find(_=>true)

Функция myfun возвращает None или Some, flatten выбрасывает все None и find возвращает первый элемент списка, если таковой имеется.

Это кажется мне немного взломанным. Я думаю, что может существовать какое-то осознание или подобное, что сделает это немного менее расточительным или более умным. Например: мне не нужны последующие ответы, если myfun возвращает Some в течение map списка l.

4b9b3361

Ответ 1

Как насчет:

l.toStream flatMap (myfun andThen (_.toList)) headOption

Поток ленив, поэтому он не будет отображать все заранее, но он тоже не переделает. Вместо сглаживания вещей, преобразуйте Option в List, чтобы можно было использовать flatMap.

Ответ 2

Ну, это почти, но не совсем

val x = (l flatMap myfun).headOption

Но вы возвращаете из myfun Option вместо List, поэтому это может не сработать. Если это так (у меня нет REPL для руки), попробуйте вместо этого:

val x = (l flatMap(myfun(_).toList)).headOption

Ответ 3

Ну, эквивалент для понимания довольно прост

(for(x<-l, y<-myfun(x)) yield y).headOption

который, если вы действительно выполняете перевод, работает так же, как и то, что дал oxbow_lakes. Предполагая разумную лень List.flatmap, это и чистое и эффективное решение.

Ответ 4

По состоянию на 2017 год предыдущие ответы кажутся устаревшими. Я провел несколько тестов (список из 10 миллионов интов, первый матч примерно посередине, Scala 2.12.3, Java 1.8.0, 1.8 ГГц Intel Core i5). Если не указано иное, list и map имеют следующие типы:

list: scala.collection.immutable.List
map: A => Option[B]

Просто позвоните map в список: ~ 1000 мс

list.map(map).find(_.isDefined).flatten

Первый вызов toStream в списке: ~ 1200 мс

list.toStream.map(map).find(_.isDefined).flatten

Вызвать toStream.flatMap в списке: ~ 450 мс

list.toStream.flatMap(map(_).toList).headOption

Вызвать flatMap в списке: ~ 100 мс

list.flatMap(map(_).toList).headOption

Первый вызов iterator в списке: ~ 35 мс

list.iterator.map(map).find(_.isDefined).flatten

Рекурсивная функция find(): ~ 25 мс

def find[A,B](list: scala.collection.immutable.List[A], map: A => Option[B]) : Option[B] = {
  list match {
    case Nil => None
    case head::tail => map(head) match {
      case None => find(tail, map)
      case result @ Some(_) => result
    }
  }
}

Итеративная функция find(): ~ 25 мс

def find[A,B](list: scala.collection.immutable.List[A], map: A => Option[B]) : Option[B] = {
  for (elem <- list) {
    val result = map(elem)
    if (result.isDefined) return result
  }
  return None
}

Вы можете ускорить работу, используя Java вместо коллекций Scala и менее функциональный стиль.

Перечисление индексов в java.util.ArrayList: ~ 15 мс

def find[A,B](list: java.util.ArrayList[A], map: A => Option[B]) : Option[B] = {
  var i = 0
  while (i < list.size()) {
    val result = map(list.get(i))
    if (result.isDefined) return result
    i += 1
  }
  return None
}

Переключить индексы в java.util.ArrayList с функцией, возвращающей null вместо None: ~ 10 мс

def find[A,B](list: java.util.ArrayList[A], map: A => B) : Option[B] = {
  var i = 0
  while (i < list.size()) {
    val result = map(list.get(i))
    if (result != null) return Some(result)
    i += 1
  }
  return None
}

(Конечно, обычно можно объявить тип параметра как java.util.List, а не java.util.ArrayList. Я выбрал последнее здесь, потому что это класс, который я использовал для тестов. Другие реализации java.util.List будут показывать разную производительность - большинство будет хуже.)