Zapowiadam teraz ciekawszy temat dotyczący Kotlina bowiem powrócimy do pętli, ale nie będziemy się nią zajmować bezpośrednio. Jest coś takiego jak "etykieta pętli" w języku Kotlin! Coś, co może znowu przypominać strzępek historii języka C. Zaciekawieni?

ETYKIETA PĘTLI W JĘZYKU KOTLIN JEST KOLEJNYM NAWIĄZANIEM DO CZEGOŚ, CO JUŻ BYŁO

Zaczniemy teraz od końca, czyli od fragmentu historii. Język C jest jednym z języków "starej daty" (geneza sięga przełomu lat .60 i .70 XX wieku). Tam występowało takie "goto", czyli prastara instrukcja skoku, która była wykorzystywana nagminnie w językach wokół których dominowało programowanie niestrukturalne. Jest to taki paradygmat, w którym cały program mieści się w głównej funkcji "main" i nie ma absolutnie żadnego podziału na jakiekolwiek funkcje (przeciwieństwem tego jest programowanie strukturalne). Wspomniana instrukcja skoku powodowała natychmiastowe "wejście" na konkretny wiersz w kodzie źródłowym w miejsce pozostawionej "etykiety", czyli nazwy pisanej po ludzku poprzedzonej jakimś symbolem (tam po znaku dwukropka), choć w starszych językach ponoć pozostawiano programistom jedynie skorzystanie z liczb.

W tym momencie wyjaśniłem Wam do czego służy etykieta pętli w języku Kotlin. Do "oflagowania" pętli w celu umożliwienia skorzystania z niej razem z instrukcją dotyczącą jej samej, taką jak "break", "continue" albo "return", w celu dania do zrozumienia od której pętli ma ona wywierać wpływ. Może być cholernie przydatna podczas programowania pętli zagnieżdżonych, ponieważ jedna z powyższych instrukcji bez pozostawionej etykiety, będzie dotyczyć tylko pętli najbardziej "wgłębionej" w środek. Gdyby nasze intencje polegały na wyjściu ze wszystkich pętli pod rząd, to tylko poprzez etykietę i właśnie po to się to stosuje.

Miejcie tylko na uwadze, że to jedynie nawiązuje do wspomnianej instrukcji skoku. Takiego czegoś samego w sobie NIE MA i nie można tego używać tam, gdzie się żywnie podoba. To dotyczy tylko łatwiejszego "wychodzenia" z zagnieżdżonych pętli.

PRZYKŁAD KODU ŹRÓDŁOWEGO

Służę pomocą przy przykładzie użycia. Dwie pętle "for", jedna "siedząca" w drugiej:

for (x in 1..3)
{
	for (y in 1..3)
	{
		println("($x, $y)")
	}
}

Przypuśćmy, że chcemy aby w pewnej sytuacji opatrzonej instrukcją warunkową, doszło do przerwania wykonywania obu pętli. To nie przejdzie:

for (x in 1..3)
{
	for (y in 1..3)
	{
		if(y == 3)
		{
			break
		}
		
		println("($x, $y)")
	}
}

gdyż to "break" zostanie zastosowane wobec pętli tej najbardziej "siedzącej wgłąb", czyli wewnętrznej w naszym przypadku. Powtórzę: chcemy, aby doszło do przerwania OBU pętli, jednej i drugiej. Etykieta pętli w języku Kotlin zakończy sprawę:

nestedLoop@
for (x in 1..3)
{
	for (y in 1..3)
	{
		if(y == 3)
		{
			break@nestedLoop
		}
		
		println("($x, $y)")
	}
}

Stwierdzenie "kij ma zawsze dwa końce", pasuje tutaj jak ulał. Aby ta sztuczka się udała, ten sam identyfikator musi się znaleźć dwukrotnie. Za pierwszym razem oznaczamy początek, czyli jak daleko ma się zaczynać uwzględnianie instrukcji. Znak "małpki" (@) jest symbolem dla kompilatora od którego znaku należy "traktować" tekst jako etykietę, a nie jak części instrukcji kodu. Za drugim razem zostawiamy tę samą etykietę tuż obok instrukcji "break", "return", bądź "continue" BEZ SPACJI, czyli jednej z instrukcji mających związek z wywieraniem wpływu na pętlę. Koniecznie uczulam Was na położenie znaków małpki. Po jednej stronie znajduje się ona na końcu, po drugiej na początku, pomiędzy słowem kluczowym, a etykietą (prefiks). To tak jak z komentarzem wielowierszowym (jak przypomnicie sobie zapis i kolejność znaków, zrozumiecie).

NIEJAWNE ETYKIETY PĘTLI

Etykieta pętli w języku Kotlin występuje także w funkcjach wyższego rzędu! Tylko tak jakby...niejawnie. Weźmy pod lupę taką funkcję "forEach" na przykład:

fun printSum(ints : Array<Int>)
{
	var result = 0

	ints.forEach {
		if(it == 0)
		{
			return@forEach
		}

		result += it
	}

	println("Rezultat wynosi $result.")
}

Kod prezentuje funkcję sumującą liczby i wypisującą rezultat (wiem, że można to napisać o wiele lepiej), z tym że potrafi odrzucać argumenty równe zero, które nie mają wpływu na wynik. Najważniejsze dla mnie jest, abyście popatrzyli na instrukcję "return" "z przyklejoną" etykietą niejawną. Zwróćcie uwagę, że nie ma nigdzie tej pierwszej ustalającej start! Właśnie o to chodzi. Funkcja "forEach" posiada swoją o tej samej nazwie. Teraz taki będzie efekt, że kiedy pętla natrafi na liczbę 0, to pomija ją i nie dodaje niczego. Czyli de facto to będzie zachowywać się jak "continue", gdyż etykieta przerwie aktualną iterację, ale całej pętli nie. Gdyby w tym przypadku zostawić samo "return" bez etykiety, to spowodowałoby to przerwanie całej procedury, czyli nie tylko aktualnej iteracji, ale także samej pętli oraz CAŁEJ FUNKCJI, czyli wszystkie kolejne instrukcje też nie zostałyby wykonane.

Żebyśmy się dobrze rozumieli. Nie ma przeciwwskazań do tego, aby wprowadzić własną etykietę która będzie teraz dotyczyć wyrażenia lambda:

fun printSum(ints : Array<Int>)
{
	var result = 0

	ints.forEach custom@ {
		if(it == 0)
		{
			return@custom
		}

		result += it
	}

	println("Rezultat wynosi $result.")
}

Efekt taki sam. Jeszcze jedno. W funkcjach wyższego rzędu, etykieta pętli "dogada się" wyłącznie z instrukcją "return". Przy całej reszcie, błąd!

Etykieta pętli w języku Kotlin

Etykieta pętli w języku Kotlin nawiązuje poniekąd do instrukcji skoku, gdyż przy użyciu nadal oryginalnej składni, decyduje ona o tym, od którego miejsca kontynuować wykonywanie kodu.


Skończone. Temat, który spokojnie można włożyć do szufladki z tematami pobocznymi, bo to nie jest żadnym fundamentem niezbędnym do świadomego programowania. Niemniej jednak uważam, że warto znać taki "smaczek", zwłaszcza jeśli może pomóc nam skrócić kod i go zwyczajnie uprościć jak równanie matematyczne.

PODOBNE ARTYKUŁY