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
.
Ответы
Ответ 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
будут показывать разную производительность - большинство будет хуже.)