Как «найти» элемент последовательности и предикатировать результат сразу? - PullRequest
2 голосов
/ 03 декабря 2011

Предположим, у меня есть функция f(n:Int):Option[String]. Я хотел бы найти такое 1 <= k <= 10, что f(k) не None. Я могу кодировать его следующим образом:

(1 to 10).find(k => f(k).isDefined)

Теперь я хотел бы знать и k, и f(k).

val k = (1 to 10).find(f(_).isDefined)
val s = f(k)

К сожалению, этот код вызывает f(k) дважды. Как бы вы нашли k и f(k) одновременно?

Ответы [ 3 ]

9 голосов
/ 03 декабря 2011

Моя первая попытка будет:

(1 to 10).view map {k => (k, f(k))} find {_._2.isDefined}

Использование view позволяет избежать создания промежуточного map. Или еще лучше с сопоставлением с образцом и частичной функцией:

(1 to 10).view map {k => (k, f(k))} collectFirst {case (k, Some(v)) => (k, v)}

Возвращает Option[(Int, java.lang.String)] (None, если не найдено ни одного элемента, удовлетворяющего f).

Вы также можете поэкспериментировать с .zipWithIndex.

2 голосов
/ 04 декабря 2011

Немного более подробный вариант решения Томаша Нуркевича:

xs = (1 to 10).view      
xs zip { xs map { f(_) } } collectFirst { case (k, Some(v)) => (k, v) }
2 голосов
/ 04 декабря 2011

Немного короче - просто карта и найти:

// for testing
def f (n: Int): Option [String] = 
  if (n > 0) Some ((List.fill (n) ("" + n)).mkString) else None

(-5 to 5).map (i => (i, f(i))).find (e => e._2 != None) 

// result in REPL
res67: Option[(Int, Option[String])] = Some((1,Some(1)))
...