2016-10-02 5 views
1

Учитывая коллекцию, coll: C[A] и функцию f: A => Option[B], в чем идиоматический способе в Scala, чтобы получить первый элемент в coll, для которого f определяется без оценки всей коллекции?Найти первый Defined элемент в коллекции в Scala

Вот моя подпись предназначена:

def findFirstDefined[A, B](coll: Traversable[A])(f: A => Option[B]): Option[B] 

Наивный подход, как coll.flatMap(f).headOption бы оценить всю коллекцию. Мы можем сделать coll.view.flatMap(f).headOption или coll.collectFirst(Function.unlift(f)), но есть ли что-то еще в стандартной библиотеке или в литературе по функциональному программированию или в скалязе/котах, что позволило бы мне это сделать?

+0

Я бы просто сделать 'coll.find (F (_) isDefined) 'не уверен, есть ли какое-либо довольно функциональное решение. –

+2

'coll.view.flatMap (f) .headOption' не компилируется (монады не объединяются), но что не так с' coll.collectFirst (Function.unlift (f)) '? Кажется хорошим решением для меня. –

+1

@ Łukasz Вам нужно будет дважды называть 'f' для найденного элемента. '.collectFirst (Function.unlift (f))' лучше в этом аспекте. –

ответ

0

Как упомянуто в вопросе, это похоже на приличном решение:.

def findFirstDefined[A, B](coll: Traversable[A])(f: A => Option[B]): Option[B] = 
    coll.collectFirst(Function.unlift(f)) 
0

Как насчет использования find(p: A => Boolean)?

def findFirstDefined[A, B](coll: Traversable[A])(f: A => Option[B]): Option[B] = 
    coll.find(f(_).isDefined).flatMap(f(_)) 

Это требует два вызова f.apply(), но не будет вызывать только apply(), пока первый элемент возвращает определенный вариант будет найден.

Edit: думать об этом (но Виктор бил меня к одному), collectFirst() более элегантный вариант, а также:

coll.collectFirst(Function.unlift(f)) 
2

IMO coll.collectFirst(Function.unlift(f)) выглядит как хорошее решение, если вы хотите использовать что-то стандарт. Но это довольно легко реализовать с помощью рекурсии:

@annotation.tailrec 
def findFirstDefined[A, B](coll: Traversable[A])(f: A => Option[B]): Option[B] = 
    if (coll.isEmpty) None 
    else { 
    val r = f(coll.head) 
    if (r.isEmpty) findFirstDefined(coll.tail)(f) 
    else r 
    } 
+0

'collectFirst' не оценивает дважды, потому что использует' applyOrElse'. Вероятно, скаладок для функции Function.unlift дает неверное впечатление. –

+0

@ som-snytt Я сказал, что 'f' дважды оценивается в предложении Лукаша и упоминается' collectFirst ... '- лучший способ именно из-за 'applyOrElse'. –

+0

Да, мне пришлось снова понять эту часть, поэтому я упоминаю ее для следующего человека. –

 Смежные вопросы

  • Нет связанных вопросов^_^