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

Как получить один элемент из цикла for в scala?

Как и этот вопрос:

Функциональный код для цикла с ранним выходом

Скажите, что код

def findFirst[T](objects: List[T]):T = {
  for (obj <- objects) {
    if (expensiveFunc(obj) != null) return /*???*/ Some(obj)
  }
  None
}

Как получить один элемент из цикла for, подобный этому, в scala?

Я не хочу использовать find, как было предложено в исходном вопросе, мне интересно, если и как это можно реализовать с помощью цикла for.

* ОБНОВЛЕНИЕ *

Во-первых, спасибо за все комментарии, но я думаю, я не был ясен в вопросе. Я снимаю что-то вроде этого:

val seven = for {
    x <- 1 to 10
    if x == 7
} return x

И это не компилируется. Две ошибки: - определение внешнего метода обратной связи - метод main имеет оператор возврата; требуется тип результата

Я знаю, что find() будет лучше в этом случае, я просто изучаю и изучаю язык. И в более сложном случае с несколькими итераторами я думаю, что поиск с помощью может действительно быть полезным.

Спасибо комментаторам, я заработаю щедрость, чтобы компенсировать плохую постановку вопроса:)

4b9b3361

Ответ 1

Вы можете превратить свой список в поток, чтобы любые фильтры, которые содержат for-loop, оценивались по запросу. Однако уступка из потока всегда будет возвращать поток, и вы хотите, я полагаю, вариант, поэтому в качестве последнего шага вы можете проверить, имеет ли результирующий поток хотя бы один элемент и возвращает свою голову в качестве опции. Функция headOption делает именно это.

def findFirst[T](objects: List[T], expensiveFunc: T => Boolean): Option[T] =
    (for (obj <- objects.toStream if expensiveFunc(obj)) yield obj).headOption

Ответ 2

Если вы хотите использовать цикл for, который использует более симпатичный синтаксис, чем цепочечные вызовы .find, .filter и т.д., есть аккуратный трюк. Вместо того, чтобы повторять строгие коллекции, такие как список, перебирайте ленивых, таких как итераторы или потоки. Если вы начинаете со строгой коллекции, сделайте ее ленивой, например. .toIterator.

Посмотрим пример.

Сначала давайте определим "шумный" int, который покажет нам, когда он вызывается

def noisyInt(i : Int) = () => { println("Getting %d!".format(i)); i }

Теперь добавьте список с некоторыми из них:

val l = List(1, 2, 3, 4).map(noisyInt)

Мы хотим найти первый элемент, который является четным.

val r1 = for(e <- l; val v = e() ; if v % 2 == 0) yield v

Вышеприведенная строка приводит к:

Getting 1!
Getting 2!
Getting 3!
Getting 4!
r1: List[Int] = List(2, 4)

... означает, что все элементы были доступны. Это имеет смысл, учитывая, что итоговый список содержит все четные числа. На этот раз на итераторе перейдем:

val r2 = (for(e <- l.toIterator; val v = e() ; if v % 2 == 0) yield v)

Это приводит к:

Getting 1!
Getting 2!
r2: Iterator[Int] = non-empty iterator

Обратите внимание, что цикл был выполнен только до точки, если бы он мог выяснить, был ли результат пустым или непустым итератором.

Чтобы получить первый результат, вы можете просто вызвать r2.next.

Если вы хотите получить результат типа Option, используйте:

if(r2.hasNext) Some(r2.next) else None

Изменить Второй пример в этой кодировке:

val seven = (for {
    x <- (1 to 10).toIterator
    if x == 7
} yield x).next

... конечно, вы должны быть уверены, что всегда есть хотя бы решение, если вы собираетесь использовать .next. В качестве альтернативы используйте headOption, определенный для всех Traversable s, чтобы получить Option[Int].

Ответ 3

Итак, что вы пытаетесь сделать, это вырвать цикл после того, как ваше условие будет удовлетворено. Ответ здесь может быть тем, что вы ищете. Как выйти из цикла в Scala?.

В целом, для понимания в Scala переведено на карту, плоскую карту и операции фильтра. Таким образом, не удастся вырваться из этих функций, если вы не сделаете исключение.

Ответ 4

Почему бы не сделать то, что вы набросали выше, то есть return из цикла раньше? Если вас интересует то, что Scala действительно делает под капотом, запустите свой код с помощью -print. Scala desugares цикл в foreach, а затем использует исключение, чтобы преждевременно оставить foreach.

Ответ 5

Если вам интересно, вот как поиск осуществляется в LineerSeqOptimized.scala; который наследует

override /*IterableLike*/
  def find(p: A => Boolean): Option[A] = {
    var these = this
    while (!these.isEmpty) {
      if (p(these.head)) return Some(these.head)
      these = these.tail
    }
    None
  }

Ответ 6

Это ужасный хак. Но это принесет вам результат, который вы пожелаете.

Идиоматически вы используете Stream или View и просто вычисляете нужные вам части.

def findFirst[T](objects: List[T]): T = {

def expensiveFunc(o : T)  = // unclear what should be returned here

case class MissusedException(val data: T) extends Exception

try {
  (for (obj <- objects) {
    if (expensiveFunc(obj) != null) throw new MissusedException(obj)
  })
  objects.head // T must be returned from loop, dummy
} catch {
  case MissusedException(obj) => obj
}

}

Ответ 7

Почему не что-то вроде

object Main {     
  def main(args: Array[String]): Unit = {
    val seven = (for (
    x <- 1 to 10
    if x == 7
    ) yield x).headOption
  }
}

Переменная seven будет содержать опцию Some(value), если значение удовлетворяет условию

Ответ 8

Я надеюсь вам помочь.

Я думаю... нет 'return' impl.

object TakeWhileLoop extends App {
    println("first non-null: " + func(Seq(null, null, "x", "y", "z")))

    def func[T](seq: Seq[T]): T = if (seq.isEmpty) null.asInstanceOf[T] else
        seq(seq.takeWhile(_ == null).size)
}

object OptionLoop extends App {
    println("first non-null: " + func(Seq(null, null, "x", "y", "z")))

    def func[T](seq: Seq[T], index: Int = 0): T = if (seq.isEmpty) null.asInstanceOf[T] else
        Option(seq(index)) getOrElse func(seq, index + 1)
}

object WhileLoop extends App {
    println("first non-null: " + func(Seq(null, null, "x", "y", "z")))

    def func[T](seq: Seq[T]): T = if (seq.isEmpty) null.asInstanceOf[T] else {
        var i = 0
        def obj = seq(i)
        while (obj == null)
            i += 1
        obj
    }
}

Ответ 9

objects iterator filter { obj => (expensiveFunc(obj) != null } next

Трюк состоит в том, чтобы получить ленивый оцененный вид на colelction, либо итератор, либо Stream, или objects.view. Фильтр будет выполняться только по мере необходимости.