Как вернуть рано без выписки? - PullRequest
3 голосов
/ 27 сентября 2019

Как написать фрагмент кода с ранним возвратом в scala без возвратов / разрывов?

Например

for i in 0..10000000
  if expensive_operation(i)
     return i
return -1

Ответы [ 5 ]

8 голосов
/ 27 сентября 2019

Как насчет

 input.find(expensiveOperation).getOrElse(-1)
6 голосов
/ 27 сентября 2019

Вы можете использовать dropWhile

Вот пример:

Seq(2,6,8,3,5).dropWhile(_ % 2 == 0).headOption.getOrElse(default = -1) // -> 8

А здесь вы найдете еще scala-takewhile-example

С вашим примером

(0 to 10000000).dropWhile(!expensive_operation(_)).headOption.getOrElse(default = -1)`
3 голосов
/ 27 сентября 2019

Поскольку вы попросили интуицию, чтобы решить эту проблему в общем.Позвольте мне начать с основы.

Scala - это (между прочим) функциональный язык программирования, поэтому для нас существует очень важная концепция.И дело в том, что мы пишем программы, составляя expressions, а не statements.

Таким образом, концепция возвращаемого значения для нас означает оценку expression.
(Примечание.это связано с понятием ссылочная прозрачность ) .

val a = expr   // a is bounded to the evaluation of expr,
val b = (a, a) // and they are interchangeable, thus b === (expr, expr)

Как это относится к вашему вопросу.В том смысле, что у нас действительно нет структур управления, но есть сложные выражения.Например, if

val a = if (expr) exprA else exprB // if itself is an expression, that returns other expressions.

Таким образом, вместо того, чтобы делать что-то вроде этого:

def foo(a: Int): Int =
  if (a != 0) {
    val b = a * a
    return b
  }
  return -1

Мы бы сделали что-то вроде:

def foo(a: Int): Int =
  if (a != 0)
    a * a
  else
   -1

Потому что мы можемсвязать все выражение if как тело foo.


Теперь вернемся к вашему конкретному вопросу.Как мы можем досрочно вернуть cycle?
Ответ: вы не можете, по крайней мере, не без мутаций.Но вы можете использовать более высокую концепцию, вместо итерации , вы можете пройти что-то.И вы можете сделать это, используя recursion.

Таким образом, давайте реализуем find, предложенный @Thilo, как tail-recursive функцию.
(Очень важно, чтобыфункция является рекурсивной по хвосту, поэтому компилятор оптимизирует ее как нечто эквивалентное циклу while, чтобы мы не взорвали стек) .

def find(start: Int, end: Int, step: Int = 1)(predicate: Int => Boolean): Option[Int] = {
  @annotation.tailrec
  def loop(current: Int): Option[Int] =
    if (current == end)
      None // Base case.
    else if (predicate(current))
      Some(current) // Early return.
    else
      loop(current + step) // Recursive step.
  loop(current = start)
}

find(0, 10000)(_ == 10)
// res: Option[Int] = Some(10)

Или мы можем немного обобщить этоподробнее, давайте реализуем поиск для списков любых типов элементов.

def find[T](list: List[T])(predicate: T => Boolean): Option[T] = {
  @annotation.tailrec
  def loop(remaining: List[T]): Option[T] =
    remaining match {
      case Nil                      => None
      case t :: _ if (predicate(t)) => Some(t)
      case _ :: tail                => loop(remaining = tail)
    }
  loop(remaining = list)
}
2 голосов
/ 27 сентября 2019

Это не обязательно лучшее решение с практической точки зрения, но я все же хотел добавить его в образовательных целях:

import scala.annotation.tailrec

def expensiveOperation(i: Int): Boolean = ???

@tailrec
def findFirstBy[T](f: (T) => Boolean)(xs: Seq[T]): Option[T] = {
   xs match {
       case Seq() => None
       case Seq(head, _*) if f(head) => Some(head)
       case Seq(_, tail@_*) => findFirstBy(f)(tail)
   }
}

val result = findFirstBy(expensiveOperation)(Range(0, 10000000)).getOrElse(-1)

Пожалуйста, предпочтите методы коллекций (dropWhile, find, ...) в вашем производственном коде.

1 голос
/ 27 сентября 2019

Здесь можно найти гораздо лучший ответ, но я думаю, что "while" может отлично сработать в этой ситуации.

Итак, этот код

for i in 0..10000000
  if expensive_operation(i)
     return i
return -1

можно переписать как

var i = 0
var result = false
while(!result && i<(10000000-1)) {
    i = i+1
    result = expensive_operation(i)
}

После 'while' переменная 'result' сообщит, успешно или нет.

...