Dlaczego Scala powrócił, ale nie pękł i kontynuował

22

Scala nie ma breaklub continue, więc pewne zachowanie w pętli wymaga nieco więcej myślenia.

Wcześniejsze zakończenie pętli wymaga rekurencji ogona, wyjątków lub scala.util.control.Breaks(która korzysta z wyjątków).

Uzasadnieniem tego jest to, gotoże są to konstrukcje przepływowe, które zaciemniają przepływ i mogą być osiągnięte w lepszy, mniej zaskakujący sposób.

Wygląda jednak na to, że można zastosować te same argumenty return.

Dlaczego Scala celowo pomijają breaki continue, ale nie return?

Paul Draper
źródło
1
Mogę sobie wyobrazić, że autorzy języku rozważyć rekurencji ogon jak na drodze do skonstruowania iteracji. Mogę to sobie wyobrazić breaki continuepotrzebuję dodatkowych maszyn do czyszczenia. OTOH returnto sposób na uporządkowane zakończenie funkcji, a każda maszyna czyszcząca już tam jest.
9000
1
Jest breakable { for { break; } }tylko refleksja i prawdopodobnie daleka od skuteczności.
Joop Eggen
Ponieważ z funkcjami tak naprawdę nie ma tego powodu. Tak samo jest w pythonie. Za każdym razem, gdy używasz pętli for z break, możesz zamiast tego napisać funkcję, umieścić w niej pętlę i użyć return. Nie mogę wymyślić sytuacji, w której nie jest to dobry pomysł dotyczący czystego kodu. Jeśli chodzi o wydajność, czyszczenie może być lepsze, ale wydajność nie ma najwyższego priorytetu w scala.
valenterry
2
To pytanie wygląda tak, jakby zawierało odpowiedź tutaj: stackoverflow.com/questions/3770989/...
Michael Shaw
3
@PaulDraper: Odpowiedź na breaki continuejest zawarta w swoim pytaniu w link w swoim pytaniu. Pytanie returnbrzmi dokładnie o to, co łączyłem z pytaniem, na które udzielono odpowiedzi, przynajmniej w najczęściej głosowanej, zaakceptowanej odpowiedzi. Jeśli dwie odpowiedzi razem nie odpowiadają na twoje pytanie, być może mógłbyś edytować pytanie, aby je wyjaśnić.
Michael Shaw

Odpowiedzi:

16

Przerwij i kontynuuj:

W rozmowie o Scali Martin Odersky podał 3 powody, dla których nie należy przerywać ani kontynuować slajdu 22:

  • Są nieco konieczne; lepiej użyj wielu mniejszych funkcji.
  • Problemy z interakcją z zamknięciami.
  • Nie są potrzebne!

A potem mówi: „Możemy wspierać ich wyłącznie w bibliotekach”. Na slajdzie 23 podaje kod, który implementuje break. Chociaż nie do końca znam Scalę wystarczająco dobrze, aby się upewnić, wygląda na to, że krótki fragment kodu na tym slajdzie jest wszystkim, co jest potrzebne do zaimplementowania break, i continuemoże być zaimplementowany w podobnie krótkim kodzie.

Możliwość implementowania takich bibliotek w bibliotekach upraszcza podstawowy język.

W „Programowaniu w Scali, wydanie drugie” Martina Odersky'ego, Lexa Spoona i Billa Vennersa podano następujące wyjaśnienie:

Być może zauważyłeś, że nie wspomniano o breakani continue. Scala pomija te polecenia, ponieważ nie łączą się dobrze z literałami funkcyjnymi ... Jest jasne, co continueoznacza wewnątrz whilepętli, ale co by to znaczyło w literałach funkcyjnych? ... Istnieje wiele sposobów programowania bez breaki continue, a jeśli skorzystasz z literałów funkcji, te alternatywy mogą być często krótsze niż oryginalny kod.

Powrót:

Zwroty mogą być uważane za nieco imperatywne, ponieważ return jest czasownikiem, poleceniem zrobienia czegoś. Ale można je również postrzegać w sposób czysto funkcjonalny / deklaratywny: definiują one wartość zwracaną przez funkcję (nawet jeśli w funkcji z wieloma zwrotami każda z nich podaje tylko częściową definicję).

W tej samej książce mówią o return:

W przypadku braku wyraźnego returnwyrażenia metoda Scala zwraca ostatnią wartość obliczoną przez metodę. Zalecanym stylem metod jest w rzeczywistości unikanie wyraźnych, a zwłaszcza wielokrotnych returninstrukcji. Zamiast tego pomyśl o każdej metodzie jako wyrażeniu, które zwraca jedną wartość, która jest zwracana.

Metody kończą i zwracają wartość, nawet jeśli returninstrukcja nie jest używana, więc nie może być problemów z zamknięciami, ponieważ w przeciwnym razie zamknięcia nie zadziałałyby.

Nie może być również problemu z zazębianiem się z literałami funkcji, ponieważ funkcja i tak musi zwrócić wartość.

Michael Shaw
źródło
2
Jeśli chodzi o powrót, wydaje się, że istnieją pewne łagodne niebezpieczeństwa: tpolecat.github.io/2014/05/09/return.html
bbarker 24.09.16
0

Myślę, że poprzednie odpowiedzi oddają sprawiedliwość problemom definiowania semantyki dla breaklub continuew języku dla Scali, przy względnie nieograniczonych kontekstach.

Napisałem małą bibliotekę , która definiuje breaki continuebardziej ograniczonego kontekstu: iterację po sekwencji za pośrednictwem Scala-listowe. Koncentrując się na tym kontekście, uważam, że semantyka staje się jednoznaczna i łatwa do uzasadnienia.

Biblioteka jest dostępna tutaj: https://github.com/erikerlandson/breakable

Oto prosty przykład tego, jak to wygląda w kodzie:

scala> import com.manyangled.breakable._
import com.manyangled.breakable._

scala> val bkb2 = for {
     |   (x, xLab) <- Stream.from(0).breakable   // create breakable sequence with a method
     |   (y, yLab) <- breakable(Stream.from(0))  // create with a function
     |   if (x % 2 == 1) continue(xLab)          // continue to next in outer "x" loop
     |   if (y % 2 == 0) continue(yLab)          // continue to next in inner "y" loop
     |   if (x > 10) break(xLab)                 // break the outer "x" loop
     |   if (y > x) break(yLab)                  // break the inner "y" loop
     | } yield (x, y)
bkb2: com.manyangled.breakable.Breakable[(Int, Int)] = com.manyangled.breakable.Breakable@34dc53d2

scala> bkb2.toVector
res0: Vector[(Int, Int)] = Vector((2,1), (4,1), (4,3), (6,1), (6,3), (6,5), (8,1), (8,3), (8,5), (8,7), (10,1), (10,3), (10,5), (10,7), (10,9))
eje
źródło