2012-09-24 16 views
48

Jestem początkującym programistą scala i natknąłem się na dziwne zachowanie.Powrót w 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);.... 

Przede zasadzie chcę wrócić true jeśli elem.isEmpty i count == 0. W przeciwnym razie chcę zwrócić false.

Teraz powyżej przeczytałem, że nie ma potrzeby dodawania zwrotu w scala. Tak więc pominąłam return powyżej. Ale nie zwraca wartości boolowskiej. Jeśli dodaję instrukcję return jako return true. działa idealnie. Dlaczego tak jest?

Ponadto, dlaczego jest ono uważane za złą praktyką mieć sprawozdań powrotne w Scala

+1

Istnieje ** zazwyczaj ** nie ma potrzeby stosowania słowa kluczowego return, o ile złamiesz kod na wystarczająco małe metody. – mauhiz

+0

@mauhiz Dzięki. Czy możesz to wyjaśnić? Jak to zrobisz. – Jatin

+2

wygląda na to, że bierzesz kurs Coursera scala. wszystko co najlepsze :) – weima

Odpowiedz

90

To nie jest tak proste, jak to tylko pominięcie słowa kluczowego return. W programie Scala, jeśli nie ma wartości return, za ostatnie wyrażenie przyjmuje się wartość zwracaną. Jeśli więc ostatnim wyrażeniem jest to, co chcesz zwrócić, możesz pominąć słowo kluczowe return. Ale jeśli to, co chcesz zwrócić, nie jest ostatnim zdaniem, to nie będzie to, wtedy Scala nie będzie wiedziała, że ​​chcesz go zwrócić:.

Przykład:

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

Oto ostatni wyraz funkcji f jest if/else wyrażenie, którego wynikiem jest String. Ponieważ nie zaznaczono żadnego wyraźnego return, Scala wywnioskuje, że chciałeś zwrócić wynik tego wyrażenia if/else: a String.

Teraz, jeśli dodamy coś po if/else wyrażenie:

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

    if (somethingElse) 
    1 
    else 
    2 
} 

Teraz ostatnie wyrażenie jest if/else wyrażenie dające do Int. Tak więc typem zwrotu f będzie Int. Jeśli naprawdę chcemy, aby zwrócił String, to mamy kłopoty, ponieważ Scala ma nie ma pojęcia, że to jest to, co zamierzaliśmy. W związku z tym musimy to naprawić, przechowując ciąg do zmiennej i zwracając go po drugim wyrażeniu if/else lub zmieniając kolejność tak, aby część String stała się ostatnia.

Wreszcie możemy uniknąć słowa kluczowego return nawet z zagnieżdżonego wyrażenia if-else jak Twoja:

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 
    } 

}

+1

Przez wszystkich Bogów, dzięki! Przez wiele godzin walczyłem z tym samym problemem (także podczas kursu w Coursera) i nie byłem w stanie zrozumieć, dlaczego powrót był wymagany. –

+0

dla pierwszego przykładu, co się stanie, jeśli dodasz zwroty. tj. "return" A "" i "return" B "'? –

+0

@ T.Rex powróciłby do wywołującego f() z wartością "A" lub "B".Ale jak wyjaśniłem w mojej odpowiedzi. Nigdy nie używaj zwrotu w Scala. Jeśli instrukcje w Scali działają w sposób funkcjonalny. Oceniają coś z typem. Podobnie jak operator trójskładnikowy w Javie (? :). Przykład: val foo = if (mybool) "A" else "B" - foo będzie ciągiem zawierającym "A" lub "B". Podobnie myślimy o funkcji, która nie zwraca czegoś, ale ocenia wartość ostatniego wyrażenia w nim. – Grmpfhmbl

3

Nie programować Scala, ale użyć innego języka z ukrytych zwrotów (Ruby). Masz kod po swoim bloku if (elem.isEmpty) - ostatnia linia kodu jest tym, co zostało zwrócone, dlatego nie dostajesz tego, czego się spodziewasz.

EDYCJA: Oto prostszy sposób, aby napisać swoją funkcję. Wystarczy użyć logiczną wartość isEmpty i liczyć na powrót automatycznie prawda czy fałsz:

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

Dzięki. Ale chcę tylko zwrócić, jeśli elem.isEmpty && count == 0 return true else kontynuować blok. Powyższe powróci, nawet jeśli jest fałszywe. – Jatin

+0

@Jatin: Ah, nie zdawałem sobie z tego sprawy. W takim przypadku odpowiedni byłby zatem wczesny i "wyraźny" zwrot. – jmdeldin

+0

@Frank: Nie jest to również zdefiniowane w kodzie OP. Zakładałem, że to było wywołanie metody. – jmdeldin

4

domyślnie zostaną zwrócone ostatnim wyrazem funkcji. W twoim przykładzie jest inne wyrażenie po punkcie, w którym chcesz zwrócić wartość. Jeśli chcesz zwrócić wszystko przed ostatnim wyrażeniem, musisz nadal używać return.

Można zmodyfikować przykład takiego, aby powrócić do Boolean z pierwszej części

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

Nie "instrukcja". "wyrażenie" –

+1

@ViktorKlang dzięki za podpowiedź, poprawiłem to. – Tharabas

3

Nie pisz if wypowiedzi bez odpowiedniego else. Po dodaniu else do fragmentu zobaczysz, że twoje true i false są w rzeczywistości ostatnimi wyrażeniami funkcji.

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.... 
+1

Zastosowałem to, otrzymałem 3 zagnieżdżone IF i wygląda brzydko. Czy istnieje jakiś wzór, aby wyglądał ładniej? – Sergey

9

Ten temat jest nieco bardziej skomplikowany, jak opisano w dotychczasowych odpowiedziach. Ten blogpost by Rob Norris wyjaśnia to bardziej szczegółowo i podaje przykłady, kiedy użycie zwrotu spowoduje złamanie kodu (lub przynajmniej nieoczywiste efekty).

W tym miejscu chciałbym przytoczyć esencję postu. Najważniejsze stwierdzenie jest na samym początku. Wydrukuj to jako plakat i przyłóż do ściany :-)

Słowo kluczowe return nie jest "opcjonalne" ani "wnioskowane"; zmienia to znaczenie twojego programu i nigdy nie powinieneś go używać.

Daje jeden przykład, gdzie faktycznie łamie coś, kiedy inline funkcję

// 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. 

ponieważ

return wyrażenie, gdy oceniano, porzuca bieżący obliczeń i powraca do wywołującego metody, w której pojawia się return.

Jest to tylko jeden z przykładów podanych w połączonym poście i jest to najłatwiejsze do zrozumienia. Jest więcej i gorąco Cię zachęcam, abyś tam poszedł, przeczytał i zrozumiał.

Kiedy pochodzisz z języków imperatywnych, takich jak Java, może się to wydawać dziwne, ale gdy już przyzwyczaisz się do tego stylu, będzie to miało sens. Pozwolę sobie zamknąć kolejny cytat:

Jeśli znajdziesz się w sytuacji, w której myślisz, że chcesz wrócić wcześniej, musisz ponownie przemyśleć sposób, w jaki zdefiniowałeś swoje obliczenia.

+1

Nie zgadzam się z Robem, IMO nie możesz używać 'return' tylko w wyrażeniach lambda, ale jest to gówniany wybór konstrukcji w języku, kod nie powinien nawet kompilować (w pythonie jest to unikane przez' lambda' słowo kluczowe bez instrukcji return) ... w każdym innym przypadku nie widzę prawdziwego problemu z używaniem 'return', jeśli chcesz zwrócić wartość (i tak, wyjdź z wykonywania metody, ponieważ jest to, do czego zwracany jest we wszystkich językach !) – daveoncode

+0

Możesz wyrazić swoją opinię, ale wiele osób zgadza się, że używanie zwrotu w Scali jest co najmniej złym stylem. Odważę się znaleźć oficjalne dokumenty Scala doc, które używają zwrotu. To AFAIK nie jest nawet wyjaśnione w oficjalnych dokumentach, tylko w dokumencie specyfikacji (rozdział 6.20). Cytując samego Martina Odersky'ego (Programowanie w Scali) "Zalecanym stylem dla metod jest unikanie wyraźnych, a zwłaszcza wielokrotnych instrukcji return. Zamiast tego, myśl o każdej metodzie jako wyrażeniu, które daje jedną wartość, która jest zwracana." Pierwsza edycja tej książki jest dostępna za darmo na stronie http://www.artima.com/pins1ed/ – Grmpfhmbl