Wyjaśnienie leniwości
Obliczy Pan/Pani wartości dopiero wtedy, gdy będą potrzebne.
Wyjaśnienie leniwości to bezpłatna lekcja Scala for Backend Engineering & Functional Programming na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Scala for Backend Engineering & Functional Programming, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Scala for Backend Engineering & Functional Programming zawiera 4 lekcji w sumie.
Czym jest leniwa ewaluacja?
W Scali większość wartości jest obliczana ściśle: są obliczane natychmiast po zdefiniowaniu. Leniwa ewaluacja odwraca tę zasadę. Wartość leniwa jest obliczana dopiero wtedy, gdy po raz pierwszy jest potrzebna — nigdy wcześniej.
Ma to znaczenie, gdy obliczenia są kosztowne, mogą się nie powieść lub mogą w ogóle nie zostać użyte. Po co wykonywać pracę, której nikt nie potrzebuje?
Ścisłe val
Zwykłe val natychmiast oblicza wyrażenie po prawej stronie, nawet jeśli nigdy więcej nie odczytają Państwo jego wartości.
Proszę uruchomić ten przykład i zauważyć, że „computing” jest wypisywane przed „after”. Praca zostaje wykonana w momencie definicji.
object Demo extends App {
val x = { println("computing"); 42 }
println("after")
println(x)
}lazy val
Wystarczy poprzedzić val słowem lazy, aby odroczyć obliczenie do pierwszego użycia.
W tym przykładzie najpierw wypisywane jest „after”, a następnie „computing”, dopiero gdy odczytane zostanie x. Wynik jest również buforowany: ciało zostaje wykonane najwyżej raz.
object Demo extends App {
lazy val x = { println("computing"); 42 }
println("after")
println(x)
println(x)
}Parametry by-name
Parametr zadeklarowany jako => A jest przekazywany przez nazwę: argument nie jest obliczany, dopóki nie zostanie użyty wewnątrz metody.
To podstawowy mechanizm leniwej ewaluacji. LazyList używa parametrów by-name, dzięki czemu ogon nie jest obliczany, dopóki nie zażądają Państwo jego wartości.
def maybe(cond: Boolean, value: => Int): Int =
if (cond) value else 0
val r = maybe(false, { println("side"); 99 })
// "side" never prints; value was unusedPoznajemy LazyList
LazyList to leniwa sekwencja Scali (w wersji 2.13+ zastąpiła starszy Stream). Jej głowa jest obliczana zachłannie, ale ogon jest leniwy.
Elementy są tworzone na żądanie i zapamiętywane po obliczeniu, dlatego ta sama komórka nigdy nie jest obliczana ponownie.
val ll = LazyList(1, 2, 3)
println(ll)
// prints LazyList(<not computed>) until forcedWymuszanie obliczeń
LazyList pozostaje nieobliczona, dopóki nie wymuszą Państwo jej ewaluacji. Wywołanie toList, foreach lub wypisanie każdego elementu wymusza obliczenie komórek.
REPL pokazuje <not computed> dla leniwego ogona. Wymuszenie ujawnia rzeczywiste wartości.
object Demo extends App {
val ll = LazyList(1, 2, 3)
println(ll.toList)
}Operator #::
Element można dodać na początku LazyList za pomocą #::. W przeciwieństwie do :: dla list prawy operand #:: jest przekazywany by-name, więc ogon nie jest tworzony, dopóki nie okaże się potrzebny.
Pozwala to opisywać sekwencje, których dalsza część jest obliczana leniwie.
val ll = 1 #:: 2 #:: 3 #:: LazyList.empty
println(ll.head)Zapamiętywanie wyników
Po obliczeniu komórki LazyList jej wartość zostaje zapisana. Ponowny odczyt nic nie kosztuje, a wyrażenie, które ją utworzyło, nie jest wykonywane drugi raz.
Proszę uruchomić ten przykład: „calc” jest wypisywane tylko przy pierwszym dostępie do każdego wymuszonego elementu, a nie przy kolejnych odczytach.
object Demo extends App {
val ll = LazyList(1, 2).map { n =>
println("calc " + n); n * 10
}
println(ll.head)
println(ll.head)
}Leniwy ogon w praktyce
Ponieważ ogon jest leniwy, można wykonać map na LazyList, a obliczone zostaną tylko elementy, do których nastąpi dostęp.
W tym przykładzie „map” jest wypisywane tylko raz, mimo że lista ma trzy elementy, ponieważ wymuszana jest wyłącznie głowa.
object Demo extends App {
val r = LazyList(1, 2, 3).map { n =>
println("map " + n); n + 1
}
println(r.head)
}Dlaczego leniwa ewaluacja pomaga
Leniwa ewaluacja umożliwia trzy rzeczy: unikanie niepotrzebnej pracy, tworzenie nieskończonych sekwencji oraz oddzielenie sposobu opisu obliczeń od zakresu ich wykonania.
Pełny potok jest zapisywany raz, a odbiorca decyduje, ile elementów pobrać.
Uwaga
Leniwa ewaluacja ma swoje koszty. LazyList zapamiętuje wartości, więc przechowywanie odwołania do jej głowy utrzymuje w pamięci każdy wymuszony element. W przypadku ogromnych lub nieskończonych danych proszę unikać zachowywania odwołania do głowy.
Ponadto efekty uboczne w leniwym kodzie są wykonywane w nieoczekiwanych momentach, co może utrudniać debugowanie.
Szybkie sprawdzenie
Proszę sprawdzić swoją wiedzę na temat leniwej ewaluacji.
Podsumowanie
Ścisła ewaluacja wykonuje pracę natychmiast, a leniwa odracza ją do momentu, gdy okaże się potrzebna. lazy val i parametry by-name (=> A) to podstawowe mechanizmy.
LazyList wykorzystuje je w następujący sposób: zachłanna głowa, leniwy i zapamiętywany ogon, wymuszany przez toList lub dostęp do elementu. Następnie samodzielnie zbudujemy LazyList.
Często zadawane pytania
Czy lekcja „Wyjaśnienie leniwości” jest bezpłatna?
Tak — pełny tekst „Wyjaśnienie leniwości” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Scala for Backend Engineering & Functional Programming, przejdź na CoddyKit PRO. Kurs Scala for Backend Engineering & Functional Programming zawiera 4 lekcji w sumie.
Co nauczysz się w „Wyjaśnienie leniwości”?
Obliczy Pan/Pani wartości dopiero wtedy, gdy będą potrzebne. Ćwiczysz Scala for Backend Engineering & Functional Programming z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Scala for Backend Engineering & Functional Programming?
Nie wymagamy żadnego doświadczenia. Scala for Backend Engineering & Functional Programming w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.
Ile czasu zajmuje lekcja „Wyjaśnienie leniwości”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Scala for Backend Engineering & Functional Programming?
Tak. Każda lekcja Scala for Backend Engineering & Functional Programming zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Wyjaśnienie leniwości
- Budowanie LazyList
- Nieskończone strumienie
- Leniwe pobieranie i filtrowanie