Jestem początkującym programistą Scala i napotkałem dziwne zachowanie.
def balanceMain(elem: List[Char]): Boolean =
{
if (elem.isEmpty)
if (count == 0)
true;
else false;
if (elem.head == '(')
balanceMain(elem.tail, open, count + 1);....
Powyżej w zasadzie chcę zwrócić prawdę, jeśli elem.isEmpty
icount == 0
. W przeciwnym razie chcę zwrócić wartość false.
Powyżej przeczytałem, że nie ma potrzeby dodawania instrukcji return w scali. Więc pominąłem return
powyżej. Ale nie zwraca wartości logicznej. Jeśli dodam instrukcję zwrotu jako return true
. działa idealnie. Dlaczego tak się dzieje?
Ponadto, dlaczego stosowanie instrukcji powrotu w scali jest uważane za złą praktykę
Odpowiedzi:
Nie jest to tak proste, jak po prostu pominięcie
return
słowa kluczowego. W Scali, jeśli nie ma,return
to ostatnie wyrażenie jest traktowane jako wartość zwracana. Jeśli więc chcesz zwrócić ostatnie wyrażenie, możesz pominąćreturn
słowo kluczowe. Ale jeśli to, co chcesz zwrócić, nie jest ostatnim wyrażeniem, Scala nie będzie wiedział, że chcesz to zwrócić .Przykład:
def f() = { if (something) "A" else "B" }
Tutaj ostatnim wyrażeniem funkcji
f
jest wyrażenie if / else, którego wynikiem jest String. Ponieważ nie ma wyraźnegoreturn
zaznaczenia, Scala wywnioskuje, że chcesz zwrócić wynik tego wyrażenia if / else: String.Teraz, jeśli dodamy coś po wyrażeniu if / else:
def f() = { if (something) "A" else "B" if (somethingElse) 1 else 2 }
Teraz ostatnim wyrażeniem jest wyrażenie if / else, którego wynikiem jest Int. Więc zwracanym typem
f
będzie Int. Jeśli naprawdę chcieliśmy, aby zwrócił String, mamy kłopoty, ponieważ Scala nie ma pojęcia , że tak właśnie było. W związku z tym musimy to naprawić, albo przechowując ciąg znaków w zmiennej i zwracając go po drugim wyrażeniu if / else, albo zmieniając kolejność, aby część typu String występowała jako ostatnia.Wreszcie, możemy uniknąć
return
słowa kluczowego nawet w przypadku zagnieżdżonego wyrażenia if-else, takiego jak twoje: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 }
}
źródło
return "A"
ireturn "B"
?Ten temat jest właściwie trochę bardziej skomplikowany, niż opisano w dotychczasowych odpowiedziach. Ten post na blogu Roba Norrisa wyjaśnia to bardziej szczegółowo i podaje przykłady, kiedy użycie return faktycznie zepsuje twój kod (lub przynajmniej spowoduje nieoczywiste efekty).
W tym miejscu pozwolę sobie tylko zacytować istotę postu. Najważniejsze stwierdzenie znajduje się zaraz na początku. Wydrukuj to jako plakat i umieść na ścianie :-)
Podaje jeden przykład, w którym faktycznie coś psuje, gdy wstawiasz 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.
dlatego
To tylko jeden z przykładów podanych w linkowanym poście i jest on najłatwiejszy do zrozumienia. Jest ich więcej i gorąco zachęcam do pójścia tam, przeczytania i zrozumienia.
Jeśli pochodzisz z imperatywnych języków, takich jak Java, może to początkowo wydawać się dziwne, ale gdy przyzwyczaisz się do tego stylu, nabierze sensu. Zakończę kolejnym cytatem:
źródło
return
tylko w wyrażeniach lambda, ale jest to gówniany projekt w języku, kod nie powinien się nawet kompilować (w Pythonie można tego uniknąć, mająclambda
słowo kluczowe bez instrukcji return). .. w każdym innym przypadku nie widzę prawdziwego problemu w używaniu,return
jeśli chcesz zwrócić wartość (i tak, wyjść z wykonywania metody, ponieważ jest to, do czego używany jest zwrot we wszystkich językach!)Nie programuję Scali, ale używam innego języka z niejawnymi zwrotami (Ruby). Masz kod po swoim
if (elem.isEmpty)
bloku - ostatnia linia kodu jest zwracana, dlatego nie otrzymujesz tego, czego oczekujesz.EDYCJA: Tutaj jest również prostszy sposób pisania funkcji. Po prostu użyj wartości logicznej isEmpty i count, aby automatycznie zwrócić prawdę lub fałsz:
def balanceMain(elem: List[Char]): Boolean = { elem.isEmpty && count == 0 }
źródło
explicit
takim przypadku wcześniejszy i zwrot byłby odpowiedni.Nie pisz
if
oświadczeń bez odpowiedniegoelse
. Po dodaniuelse
do swojego fragmentu zobaczysz, że twojetrue
ifalse
faktycznie są 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....
źródło
Domyślnie zostanie zwrócone ostatnie wyrażenie funkcji. W twoim przykładzie jest inne wyrażenie po punkcie, w którym chcesz zwrócić wartość. Jeśli chcesz zwrócić cokolwiek przed ostatnim wyrażeniem, nadal musisz użyć
return
.Możesz zmodyfikować swój przykład w ten sposób, aby zwrócić
Boolean
z pierwszej częścidef 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 } }
źródło
Dopasowanie przypadków użycia do celów wczesnego zwrotu. Zmusi cię to do jawnego zadeklarowania wszystkich gałęzi powrotu, zapobiegając nieostrożnemu błędowi zapomnienia gdzieś o zapisaniu powrotu.
źródło