Dzisiejszy artykuł przechodzi do kolejnego rozdziału niniejszej serii jakim będzie wyrażenie lambda w języku Kotlin, potężna broń będąca nieśmiertelnym składnikiem programowania funkcyjnego. Dowiecie się co to jest, jak to wygląda, jaki takie coś posiada typ oraz jak można z tego skorzystać. Cóż innego zrobić jak nie przeczytać całości?

WYRAŻENIE LAMBDA W JĘZYKU KOTLIN NIE STANOWI PREMIERY...

...bo znane jest już z czasów 1958 roku, kiedy to wchodził język Lisp. Występuje także w samej Javie, C Sharpie oraz kilku innych językach wysokiego poziomu, zatem sam "wynalazek" posiada już kawał historii. A co za tym idzie, powinien niektórym już coś mówić.

CO TO TAKIEGO?

Wyrażenie lambda to blok kodu osadzony wewnątrz klamerek, który można wprowadzić do innej funkcji i użyć go jako parametru aktualnego. Innymi słowy, za parametr wprowadzamy nie wartość jak tradycyjnie, tylko KOD, serię instrukcji. Dzięki temu, możemy za parametr dorzucić za każdym razem niestandardowy blok instrukcji i dostosowywać przebieg działania funkcji, która tę lambdę wywołuje. To nie wszystko. Funkcja wykorzystująca lambdę może także ją zwracać jako wartość wyjściową!

Termin pochodzi od królowej nauk i definicji "rachunku lambda", w którym to wykorzystywane są krótkie anonimowe funkcje, oznaczane przy pomocy greckiej litery "lambda".

WYGLĄD WYRAŻENIA LAMBDA

Wyrażenie lambda w języku Kotlin wygląda dość charakterystycznie. Wewnątrz klamerek, osadzamy parametry (może też ich wcale nie mieć), potem tak zwany "operator strzałki" (który również powinien być już znany niektórym), a na końcu instrukcja albo ich mnóstwo, przy czym domyślnie zwracana jest wartość powstała z ostatniej instrukcji. Oto najprostszy przykład celem zademonstrowania wyglądu:

val lambda = {x : Int, y : Int -> x*y}

Wyrażenie lambda nazywane jest zamiennie "funkcją anonimową", ze względu na brak jej nazewnictwa (etykiety) i "składanie się" wyłącznie z treści, przez co nie można jej wywołać w sposób tradycyjny. Możemy ją jednak osadzić do zmiennej, przechowując w ten sposób referencję.

OPERATOR STRZAŁKI MOŻE NIE BYĆ POTRZEBNY

Nie jest koniecznie powiedziane, że wyrażenie lambda ma koniecznie mieć ten operator strzałki. Jeżeli nie zawiera ono żadnych parametrów, to wystarczą klamerki i instrukcje:

val lambda = {"Wyrażenie lambda"}

To spowoduje zwrócenie łańcucha znaków i tyle! Operator strzałki jedynie stawia "granicę" pomiędzy listą parametrów, a instrukcjami. A jeśli jest tylko te drugie, to nie trzeba tej granicy wyznaczać. Rozumie się samo przez się.

TYP WYRAŻENIA LAMBDA

Nie zastanawia Was jak może wyglądać typ lambdy? Od razu piszę, to nie będzie ŻADEN z tych poznanych do tej pory. Jest on znacznie bardziej rozbudowany i określa się go mianem "typu funkcyjnego". Weźmy pierwszy przykład:

val lambda = {x : Int, y : Int -> x*y}

Takie wyrażenie lambda w języku Kotlin będzie miało typ:

val lambda : (Int, Int) -> Int = {x : Int, y : Int -> x*y}

A wyrażenie podane w drugiej kolejności? To będzie coś takiego:

val lambda : () -> String = {"Wyrażenie lambda"}

Czyli wniosek jest taki, że na typ wyrażenia lambda składają się trzy części:

  1. para nawiasów okrągłych, w których umieszczamy ewentualne parametry (nawet kiedy nie planujemy wstawić żadnych, i tak obowiązkiem jest umieścić tę parę, tak jak było przy funkcji)
  2. operator strzałki (również wstawiamy obowiązkowo, nawet kiedy nie ma żadnych parametrów)
  3. typ zwracanej wartości

Jeśli damy kompilatorowi wystarczające informacje na temat typów parametrów i typu zwracanej wartości, możemy pominąć jawną typizację wyrażenia lambda. Natomiast zawsze możemy zmodyfikować domyślne zachowanie poprzez jawne wstawienie typu. Na przykład dla drugiego przykładu, narzucając taki typ:

val lambda : () -> Unit = {"Wyrażenie lambda"}

sprawimy, że wyrażenie lambda w języku Kotlin nie zwróci niczego, mimo tego że w środku znajduje się łańcuch znaków, gdyż wymusiliśmy typ zwracanej wartości "Unit", a przypominam że to jest tożsame z "void"!

WIELE INSTRUKCJI LAMBDY

Wyrażenie lambda może składać się również z setek linijek kodu! Przykład pierwszy z brzegu, proszę bardzo:

val lambda =
{
	val x = 25
	
	println("Wartość zmiennej przechowywanej w wyrażeniu lambda wynosi $x.")
}

Jeśli dozwolone są dwie instrukcje, to można bez problemu wstawić więcej. Jaki będzie typ?

() -> Unit

Przypominam, że jeśli lambda MA zwracać wartość, a zawiera w sobie wiele linijek kodu, to OSTATNIA INSTRUKCJA decyduje o wartości do zwrócenia!

PRZYKŁAD UŻYTECZNOŚCI

Przedstawiając fragment praktyki, pokażę jej lepsze zastosowanie aby uwypuklić jej przydatność. Dajmy na to, że mamy ochotę zaprogramować konwerter walut z PLN na inne waluty zagraniczne. Można to zrobić za pomocą serii funkcji:

fun plnToUSD(pln : Double) = pln/4.0710
fun plnToEuro(pln : Double) = pln/4.5941

tylko może to nieco zabrudzić całokształt kodu źródłowego, tym bardziej jeśli taka konwersja nie jest nam potrzebna w całym programie, tylko w jakiejś jednej funkcji. Wyrażenie lambda w języku Kotlin może w takiej sytuacji stać się naszym "superbohaterem". Otóż, po opracowaniu zmiennej, możemy utworzyć sobie lambdę obliczającą nam ile polskich złotych mieści się w jednym dolarze czy euro. Przerzućmy sobie to samo na lambdy:

val plnToUSD = {n : Double -> n/4.0710}
val plnToEuro = {n : Double -> n/4.5941}

Jakie korzyści? Na razie mizerne. Na chwilę obecną to tylko spowodowało ograniczenie istnienia naszych wyrażeń do mniejszego spektrum (występowanie tylko w ciele funkcji, w której umieściliśmy wyrażenie lambda w języku Kotlin). Pełny potencjał można za to zobaczyć kiedy osadzimy lambdę jako parametr do funkcji. Wówczas do jednej funkcji, wprowadzamy różne instrukcje, zatem jedna funkcja "ogólna" do konwersji walut pozwala na samodzielne ustalenie w jaki sposób konwertować dla aktualnego przypadku wywołania. A propos wywoływania! W jaki sposób uruchomić lambdę?

WYWOŁYWANIE WYRAŻENIA LAMBDA

To będzie ostatni punkt należący do wprowadzenia wyrażeń lambda. Wywoływanie jej odbywa się IDENTYCZNIE jak u każdej innej funkcji. Po prostu podając nazwę zmiennej, w której znajduje się wyrażenie lambda w języku Kotlin:

val euro = plnToEuro(27.20)

OK. Prawie tak samo, bo funkcja korzysta ze swojej etykiety, a lambda jej nie posiada zatem posługujemy się etykietą (czyt. "nazwą") zmiennej.

Macie jeszcze drugi sposób wywoływania lambd. Jest taka metoda "invoke", która nie daje niczego szczególnego w porównaniu do pierwszego zapisu. Robi to samo i już:

val usd = plnToUSD.invoke(35.0)
Wyrażenie lambda w języku Kotlin

Wyrażenie lambda to blok kodu pozbawiony nazwy, który może stanowić parametr dla innych funkcji, torując drogę do implementowania niestandardowych instrukcji przy każdorazowym wywołaniu.


Dotarliśmy do mety. Wiecie już, że Kotlin obsługuje także programowanie funkcyjne. Łącząc wyrażenia lambda z funkcjami, czynicie kod jeszcze bardziej profesjonalnym!

PODOBNE ARTYKUŁY