W ramach postawienia kropki nad i, jeśli chodzi o lambdy w Kotlinie, zdradzę Wam teraz jak działają niektóre funkcje wyższego rzędu w języku Kotlin.
Tweet |
FUNKCJE WYŻSZEGO RZĘDU DZIAŁAJĄ UNIWERSALNIE DZIĘKI LAMBDOM!
Poproszę Was najpierw o przeczytanie co to w ogóle funkcja wyższego rzędu, aby zachować logiczną kolejność czytania żeby potem kumać o co w tym biega. Dopiero wtedy wrócić tu.
Zakładając, że posłuchaliście...
Funkcje jakie mam zamiar przedstawić operują na kolekcjach. Takich jak lista, takich jak zbiór, takich jak mapa. Dzięki wyrażeniom lambda, podstawiamy w jednej linijce kryterium na podstawie CZEGO ma się zrealizować konkretna czynność. Jakie na przykład? Kilka z poniższych.
Dla wszystkich zamieszczonych przykładów, zakładamy że rozmawiamy o następującej liście przechowującej elementy typu "klasa danych »Jewel«":
data class Jewel(val type: String, val price: Int)
val jewels = listOf(Jewel("Szmaragd", 350), Jewel("Diament", 4500), Jewel("Topaz", 420))
MINOFORNULL
"minOfOrNull" to zwrócenie elementu typu kolekcji, które w podanym przez nas kryterium w wyrażeniu lambda (tak działają funkcje wyższego rzędu) zostało uznane za najniżej stojące w hierarchii. W powyższej sytuacji, możemy to interpretować dwojako.
Po podstawieniu "price", klasyfikujemy po wartości:
println(jewels.minOfOrNull { it.price })
A po podstawieniu nazwy, otrzymujemy element którego łańcuch znaków jest ostatni po posortowaniu w kolejności alfabetycznej (litera D "stoi" bliżej niż T i S):
println(jewels.minOfOrNull { it.type })
Jak możecie zauważyć, funkcje wyższego rzędu wymagają tylko prostej lambdy, w której osadzamy interesujące nas kryterium. Możemy pominąć nawiasy, ale tylko w wyjątkowej sytuacji. Pamiętajcie o tym, że metoda uwzględnia napotkanie na wartość "null"! Zatem, zwraca typ akceptujący wartość "null"!
MAXOFORNULL
Sprawa z funkcją "maxOfOrNull" ma się podobnie, z tym że zwraca element uznany przez klasyfikację za wysunięty na pierwszą pozycję. Żeby trzymać się klarowności, dla "price" będzie zwrócony element o największej wartości:
println(jewels.maxOfOrNull { it.price })
a co do nazwy, to "zwycięży" ten łańcuch znaków, który zaczyna się od najdalszej litery (D < S < T):
println(jewels.maxOfOrNull { it.type })
Tak jak u góry, również tutaj może być figa z makiem w przypadku komplikacji, i otrzymać wartość "null"!
SUMOF
Funkcje wyższego rzędu to nie tylko "selekcja najodpowiedniejszego". Używając "sumOf" możecie przyjemnie zsumować wszystkie wartości wskazanej właściwości (liczbowej, oczywiście). Podstawiając do naszego przykładu:
println(jewels.sumOf { it.price })
przekonamy się jaka wychodzi suma wszystkich składników podanej właściwości.
FILTER
Dostępna jest także funkcja filtrująca kolekcję na kształt zapytania w SQL. Wyrażenie lambda w tym przypadku zwraca "Boolean'a" (wartość logiczną) i oznacza ona warunek jaki musi spełnić element, żeby znalazł się na liście zwracanej przez funkcję "filter". Zobaczmy to w akcji:
println(jewels.filter { it.price > 400 })
To zwróci dwa elementy typu "Jewel", które zawierają właściwość "price" większą od czterystu. Jak wspomniałem - czysty "SELECT" w bazach danych!
MAP
Przytoczę także funkcję "map". W wyrażeniu lambda, wstawiamy teraz wyrażenie połączone z właściwością. Dla przykładu, gdybyście mieli listę liczb całkowitych i chcieli każdą z nich przemnożyć przez 10, patrzycie teraz na doskonałego kandydata, który Wam zwróci listę liczb całkowitych, a każda z nich zostanie przemnożona przez dziesięć. A o to przykład dla powyższych danych:
println(jewels.map { it.price*1.5 })
Nie musimy sobie zawracać głowy pętlą "for" i ręcznym przemnażaniem każdej wartości właściwości "price" z osobna. Wystarczy skorzystać z "map".
FOREACH
Funkcje wyższego rzędu to także pętla "for" w charakterze funkcji! Skorzystajcie z "forEach", aby w wyrażeniu lambda zawrzeć instrukcje dla wszystkich elementów jakie mają zostać wykonane. Takie wypisanie na przykład:
jewels.forEach { println(it) }
Strzeżcie się takiego wyjątku, że lambda nie zwraca żadnej wartości (typ "Unit")! Aby przechować jakiekolwiek wyniki, możecie utworzyć sobie zmienną posługując się słowem kluczowym "var" i modyfikować wartość wewnątrz lambdy.
Funkcje wyższego rzędu wymagają podania kryterium w postaci prostego wyrażenia lambda, stając się tym samym elastyczne i dostosowujące się do każdej sytuacji.
Jak widzicie, jakiej funkcji nie użyjecie, wszystkie kręcą się wokół wyrażenia lambda. To one otwierają wrota do uniwersalnego definiowania kryterium, dzięki czemu funkcja jest jedna, a możliwości nieskończona ilość. Pokazując to na przykładzie listy obiektów, widać cały potencjał tych funkcji kiedy nie da się ustalić domyślnego kryterium (pamiętacie zabawę z "compareTo"?).
Tych funkcji wyższego rzędu jest o wiele więcej. Dla tych, którzy pragną poznać je wszystkie, kieruję do dokumentacji Kotlina.