Повернення в Скалі


84

Я новачок-програміст 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);....

Вище в основному я хочу повернути істину, якщо elem.isEmptyі count == 0. В іншому випадку я хочу повернути false.

Тепер вище я прочитав, що немає необхідності додавати оператор return у масштабі. Отже, я пропустив returnвище. Але це не повертає логічну форму. Якщо я додаю оператор повернення як return true. це працює чудово. Чому це так?

Крім того, чому вважається поганою практикою мати оператори повернення в масштабі


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

@mauhiz Дякую. Ви можете пояснити це? Як ти це зробиш.
Jatin

4
схоже, ви проходите курс курсу Scala Scala. всі найкращі :)
weima

Відповіді:


139

Це не так просто, як просто пропустити returnключове слово. Якщо в Scala немає, returnто останнім виразом приймається значення, що повертається. Отже, якщо останній вираз - це те, що ви хочете повернути, тоді ви можете пропустити returnключове слово. Але якщо те, що ви хочете повернути, не є останнім виразом, то Скала не знатиме, що ви хотіли його повернути .

Приклад:

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

Тут останнім виразом функції fє вираз if / else, який обчислюється у рядку. Оскільки явного returnпозначення немає , Scala зробить висновок, що ви хотіли повернути результат цього виразу if / else: рядок.

Тепер, якщо ми додамо щось після виразу if / else:

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

  if (somethingElse)
    1
  else
    2
}

Тепер останній вираз - це вираз if / else, який обчислюється як Int. Тож тип повернення fбуде Int. Якщо ми дійсно хотіли, щоб він повернув Рядок, то ми потрапили в біду, тому що Скала не здогадується , що це саме те, що ми задумали. Таким чином, ми повинні це виправити, або зберігаючи String до змінної та повертаючи його після другого виразу if / else, або змінюючи порядок так, щоб частина String траплялася останньою.

Нарешті, ми можемо уникнути 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
  }

}


4
Усім Богам, дякую! Я годинами боровся з тією ж проблемою (також проходив курс у Куршрі) і не зміг зрозуміти, чому потрібно повернення.
Ігор Родрігес

для першого прикладу, що станеться, якщо ви додасте повернення. тобто return "A"і return "B"?
SamAko

@ T.Rex він повернеться до абонента f () зі значенням "A" або "B". Але як я пояснив у своїй відповіді. Ніколи не використовуйте повернення в Scala. Якщо оператори в Scala працюють функціонально. Вони оцінюють щось із типом. Як і тернарний оператор на Java (? :). Приклад: val foo = if (mybool) "A" else "B" - foo буде рядком, що містить або "A", або "B". Подібним чином подумайте про функцію, яка не повертає щось, а оцінює значення останнього виразу в ній.
Grmpfhmbl

23

Ця тема насправді трохи складніша, як описано у відповідях до цього часу. Цей допис блогу Роб Норріса пояснює це більш докладно та наводить приклади того, як використання return фактично порушить ваш код (або, принаймні, матиме неочевидні ефекти).

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

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.

оскільки

Під returnчас обчислення вираз відмовляється від поточного обчислення і повертається до абонента методу, в якому returnз'являється.

Це лише один із прикладів, наведених у зв’язаному дописі, і це найпростіше зрозуміти. Є ще багато, і я настійно закликаю вас зайти туди, прочитати та зрозуміти.

Коли ви походите з таких імперативних мов, як Java, спочатку це може здатися дивним, але як тільки ви звикнете до цього стилю, це матиме сенс. Дозвольте мені закінчити ще однією цитатою:

Якщо ви потрапили в ситуацію, коли ви думаєте, що хочете повернутися достроково, вам потрібно переглянути все так, як ви визначили своє обчислення.


12
Я не згоден з Роб, ІМО ви не повинні використовувати returnтільки в лямбда-виразах, але це дерьмо вибір дизайну в мові, код навіть не повинен компілюватися (у python цього уникає наявність lambdaключового слова без оператора return). .. в одних інших випадках я не бачу реальної проблеми у використанні, returnякщо ви хочете повернути значення (і так вихід із виконання методу, тому що для цього використовується повернення на всіх мовах!)
daveoncode

2
Ви можете вільно мати свою думку, але багато людей сходяться на думці, що використання повернення в Scala - це принаймні поганий стиль. Я наважуюсь вам знайти офіційні приклади документації Scala, в яких використовується return. Це AFAIK навіть не пояснено в офіційних документах, лише в специфікаційному документі (глава 6.20). Процитувавши самого Мартіна Одерського (Програмування в Scala), "Рекомендований стиль методів - насправді уникати явних, а особливо багаторазових операторів повернення. Натомість, розглядайте кожен метод як вираз, що дає одне значення, яке повертається". Перше видання цієї книги доступне безкоштовно в Інтернеті artima.com/pins1ed
Grmpfhmbl,

4

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

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

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

Дякую. Але я хочу повернути лише якщо elem.isEmpty && count == 0 return true інакше продовжуватиме блокування. Вищезазначене повернеться, навіть якщо воно хибне.
Jatin

@Jatin: Ах, не розумів цього. explicitТоді дострокове та повернення було б доречним у цьому випадку.
jmdeldin

@Frank: Це також не визначено в коді ОП. Я припустив, що це виклик методу.
jmdeldin

4

Не пишіть 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....

5
Я застосував його, отримав 3 вкладених IF, і це виглядає потворно. Чи є якийсь зразок, щоб зробити його приємнішим?
Капацитрон

4

За замовчуванням повертається останній вираз функції. У вашому прикладі є інший вираз після точки, де ви хочете повернути своє значення. Якщо ви хочете повернути що-небудь до останнього виразу, вам все одно доведеться використовувати return.

Ви можете змінити свій приклад таким чином, щоб повернути a 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
  }
}

9
Не "заява". "вираз"
Віктор Кланг,

0

Використовуйте співпадіння випадків для дострокового повернення. Це змусить вас явно декларувати всі гілки повернення, запобігаючи необережній помилці забути десь написати return.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.