Возвращение в скалу


Я новичок Scala программист и наткнулся на странное поведение.

def balanceMain(elem: List[Char]): Boolean =
  {
    if (elem.isEmpty)
      if (count == 0)
        true;
      else false;

    if (elem.head == '(')
      balanceMain(elem.tail, open, count + 1);....

выше в основном я хочу вернуть true, если elem.isEmpty и count == 0. В противном случае, я хочу вернуть false.

теперь выше я прочитал, что нет необходимости добавлять оператор return в scala. Поэтому я опустил return выше. Но он не возвращает логическое значение. Если я добавлю оператор return как return true. он прекрасно работает. Почему это так?

кроме того, почему это считается плохим практика, чтобы иметь операторы возврата в scala

5 57

5 ответов:

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

пример:

def f() = {
  if (something)
    "A"
  else
    "B"
}

вот последнее выражение функция f - это выражение if/else, которое вычисляется как строка. Так как нет явного return помечено, Scala сделает вывод, что вы хотите вернуть результат этого выражения if/else: a String.

теперь, если мы добавим что-то после выражение if/else:

def f() = {
  if (something)
    "A"
  else
    "B"

  if (somethingElse)
    1
  else
    2
}

теперь последнее выражение является выражением if/else, которое вычисляется как Int. Так что возвращаемый тип f будет Int. Если мы действительно хотели, чтобы он вернул строку, тогда мы в беде, потому что у Скалы есть идея это то, что мы задумывали. Таким образом, мы должны исправить это, либо сохранив строку в переменной и вернув ее после второго выражения if/else, либо изменив порядок так, чтобы часть строки была последней.

наконец, мы можем избежать return ключевое слово даже с вложенным выражением if-else, как у вас:

def f() = {
  if(somethingFirst) {
    if (something)      // Last expression of `if` returns a String
     "A"
    else
     "B"
  }
  else {
    if (somethingElse)
      1
    else
      2

    "C"                // Last expression of `else` returns a String
  }

}

эта тема на самом деле немного сложнее, как описано до сих пор ответы. Это блогпост Роба Норриса объясняет это более подробно и дает примеры, когда использование return фактически сломает ваш код (или, по крайней мере, будет иметь неочевидные эффекты).

на данный момент позвольте мне просто процитировать суть поста. Самое важное утверждение находится в самом начале. Распечатайте это как плакат и положите его на стену : -)

The return ключевое слово не является "необязательным" или "выводимым"; оно изменяет смысл вашей программе, и вы никогда не должны использовать его.

это дает один пример, где он на самом деле ломает что-то, когда вы вставляете функцию

// Inline add and addR
def sum(ns: Int*): Int = ns.foldLeft(0)((n, m) => n + m) // inlined add

scala> sum(33, 42, 99)
res2: Int = 174 // alright

def sumR(ns: Int*): Int = ns.foldLeft(0)((n, m) => return n + m) // inlined addR

scala> sumR(33, 42, 99)
res3: Int = 33 // um.

, потому что

A return выражение при вычислении отказывается от текущего вычисления и возвращается к вызывающему методу, в котором return появляется.

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

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

если вы окажетесь в ситуации, когда вы думаете, что хотите вернуться рано, вам нужно переосмыслить то, как вы определили свой вычисление.

Я не программирую Scala, но я использую другой язык с неявными возвращениями (Ruby). У вас есть код после if (elem.isEmpty) блок -- последняя строка кода-это то, что возвращается, поэтому вы не получаете то, что ожидаете.

EDIT: вот более простой способ написать свою функцию тоже. Просто используйте логическое значение isEmpty и count для автоматического возврата true или false:

def balanceMain(elem: List[Char]): Boolean =
{
    elem.isEmpty && count == 0
}

по умолчанию будет возвращено последнее выражение функции. В вашем примере есть еще одно выражение после точки, где вы хотите получить возвращаемое значение. Если вы хотите вернуть что-либо до вашего последнего выражения, вы все равно должны использовать return.

вы можете изменить свой пример так, чтобы вернуть Boolean из первой части

def balanceMain(elem: List[Char]): Boolean = {
  if (elem.isEmpty) {
    // == is a Boolean resulting function as well, so your can write it this way
    count == 0
  } else {
    // keep the rest in this block, the last value will be returned as well
    if (elem.head == "(") {
      balanceMain(elem.tail, open, count + 1)
    }
    // some more statements
    ...
    // just don't forget your Boolean in the end
    someBoolExpression
  }
}

не пиши if операторы без соответствующего else. Как только вы добавите else к вашему фрагменту вы увидите, что ваш true и false фактически являются последними выражениями функции.

def balanceMain(elem: List[Char]): Boolean =
  {
    if (elem.isEmpty)
      if (count == 0)
        true
      else
        false
    else
      if (elem.head == '(')
        balanceMain(elem.tail, open, count + 1)
      else....