Samouczek Golang For Loop [With Examples]

Zgłęb wiedzę na temat pętli `for` w języku Golang, analizując praktyczne przykłady kodu.

W ostatnim czasie, języki programowania takie jak Rust, Golang i TypeScript zdobywają uznanie wśród programistów. Jeśli Twoim obszarem zainteresowań jest tworzenie backendu i DevOps, warto rozważyć Golang jako doskonały wybór!

Dla osób stawiających pierwsze kroki w programowaniu, struktury pętli stanowią jedną z podstawowych koncepcji, które należy opanować.

Golang oferuje wyłącznie pętlę `for`. W dalszej części artykułu szczegółowo omówimy jej działanie oraz sposoby symulowania innych rodzajów pętli za jej pomocą.

Zacznijmy!

Składnia pętli `for` w Golang

W Golang pętlę `for` tworzy się za pomocą następującej konstrukcji:

for inicjalizacja; warunek; aktualizacja {
    // kod do wykonania
}

Gdzie:

  • `inicjalizacja` – to etap ustalania początkowej wartości zmiennej sterującej pętlą.
  • `warunek` – decyduje o tym, czy ciało pętli będzie kontynuowane. Dopóki warunek jest spełniony (ma wartość `true`), instrukcje wewnątrz pętli są wykonywane. W momencie gdy warunek przestaje być prawdziwy (przyjmuje wartość `false`), pętla zostaje zakończona.
  • `aktualizacja` – to modyfikacja zmiennej sterującej pętlą, najczęściej poprzez inkrementację (zwiększenie) lub dekrementację (zmniejszenie).

💡 Warto zauważyć, że jest to struktura zbliżona do pętli `for` w języku C, z tą różnicą, że nie używa nawiasów.

Poniżej przedstawiony jest schemat przepływu sterowania w pętli `for` w Golang:

Przejdźmy do praktyki!⏰ Aby móc kodować razem, możesz skorzystać z lokalnej instalacji Golanga lub wykorzystać Go Playground.

Przykłady pętli `for` w Golang

Wykorzystajmy poznaną składnię, aby napisać naszą pierwszą pętlę `for`. Poniżej znajduje się prosta pętla, która wyświetla liczby od 1 do 5, zwiększając licznik o 1 w każdej iteracji.

package main

import "fmt"

func main() {
	fmt.Println("Pętla for:")
	num := 5
	for i := 1; i <= num; i++ {
		fmt.Println(i)
	}
}

Inicjalizujemy zmienną `i` wartością 1, ustawiamy warunek `i <= 5` i zwiększamy `i` o 1 po każdej iteracji. Oto rezultat:

//Wyjście
Pętla for:
1
2
3
4
5

Stwórzmy kolejną pętlę `for`. Tym razem zaczniemy od 5 i będziemy odliczać w dół do 1. Pętla będzie działać tak długo, jak zmienna sterująca będzie większa lub równa 1. Po każdej iteracji zmniejszymy zmienną `i` o 1.

package main

import "fmt"

func main() {
	fmt.Println("Pętla for:")
	num := 5
	for i := num; i >= 1; i-- {
		fmt.Println(i)
	}
}

Otrzymujemy oczekiwane wyjście:

//Wyjście
Pętla for:
5
4
3
2
1

Jaki jest zakres zmiennej pętli?

Zasięg zmiennej pętli jest ograniczony do bloku pętli `for` i nie można do niej uzyskać dostępu poza tym blokiem.

Aby to potwierdzić, spróbujmy użyć wartości zmiennej `i` poza pętlą:

package main

import "fmt"

func main() {
	fmt.Println("Pętla for:")
	num := 5
	for i := 1; i <= num; i++ {
		fmt.Println(i)
	}
	fmt.Println(i)

}

Zgodnie z oczekiwaniami, wystąpił błąd, który informuje, że `i` jest niezdefiniowane (ponieważ jego zakres jest ograniczony do pętli `for`):

// Wyjście
./prog.go:11:14: undefined: i

Nieskończona pętla `for` w Golang

Czy w Go możemy tworzyć nieskończone pętle `for`? Oczywiście!

Analizując przepływ sterowania w pętli `for`:

  • Ciało pętli jest wykonywane, dopóki warunek jest prawdziwy (`true`).
  • Gdy warunek staje się fałszywy (`false`), sterowanie opuszcza pętlę.
  • Jeśli zatem warunek nigdy nie staje się fałszywy (lub zawsze jest prawdziwy), uzyskujemy nieskończoną pętlę.

Możemy także użyć pętli `for` bez inicjalizacji, warunku i aktualizacji, nie powodując błędów składniowych. Dzięki temu możemy uzyskać pętlę działającą w nieskończoność, wykorzystując konstrukcję podobną do tej:

package main

import "fmt"

func main() {
	for {
	   fmt.Println("działam...")
	}
}
//Wyjście
działam...
działam...
działam...
działam...
działam...
//i tak w nieskończoność!

W poniższym przykładzie ustawiamy zmienną `num` na 5. Warunkiem pętli jest `num > 0`. Oznacza to, że pętla będzie działać dopóki num jest większe od zera.

package main

import "fmt"

func main() {
	num := 5
	for num > 0 {
		fmt.Println(num)
	}
}

Ponieważ wartość `num` nigdy się nie zmienia, warunek jest zawsze prawdziwy, a pętla działa bez końca!

//Wyjście
5
5
5
5
5
5
//i tak w nieskończoność!

Golang ma tylko konstrukcję pętli `for`, ale za jej pomocą możemy symulować działanie pętli `while` oraz `do-while`. Zobaczmy, jak to zrobić!

Emulowanie pętli `while` za pomocą pętli `for`

Pętla `while` ma zazwyczaj następującą postać:

// inicjalizacja zmiennej sterującej
while (warunek){
    // kod do wykonania
    // aktualizacja zmiennej sterującej
}

Wspominaliśmy wcześniej, że utworzyliśmy nieskończoną pętlę `for` bez inicjalizacji, warunku i aktualizacji:

for {
// najprostsza nieskończona pętla
}

Możemy więc zmodyfikować pętlę `for`, tak aby zawierała jedynie warunek, dzięki czemu będzie przypominała pętlę `while`:

//inicjalizacja zmiennej sterującej
for warunek {
 // kod do wykonania
 // aktualizacja zmiennej sterującej
}

Poniżej znajduje się odpowiednik pętli `while` dla pierwszej pętli `for`, którą napisaliśmy:

package main

import "fmt"

func main() {
	fmt.Println("Symulacja pętli while")
	num := 5
	for num > 0 {
		fmt.Println(num)
		num--
	}
}
//Wyjście
Symulacja pętli while
5
4
3
2
1

Emulowanie pętli `do-while` za pomocą pętli `for`

Jeśli programowałeś w języku takim jak C, wiesz, że pętla `do-while` ma następującą strukturę:

// inicjalizacja zmiennej sterującej
do {
//kod do wykonania
// aktualizacja zmiennej sterującej
} while(warunek);

Podstawową różnicą między pętlami `while` i `do-while` jest to, że `while` sprawdza warunek przed wejściem do pętli, natomiast `do-while` sprawdza warunek po wykonaniu ciała pętli.

W pętli `while`, jeśli warunek jest fałszywy, ciało pętli nigdy nie zostanie wykonane. W pętli `do-while` ciało pętli wykona się przynajmniej raz, nawet jeśli warunek jest od początku fałszywy.

Mając to na uwadze, możemy symulować zachowanie pętli `do-while` poprzez:

  • Utworzenie nieskończonej pętli `for`
  • Użycie instrukcji warunkowej `if` z odpowiednim warunkiem, aby przerwać pętlę

Załóżmy, że chcemy utworzyć pętlę `do-while`, w której warunkiem wykonania ciała pętli jest `num < 0`. Możemy w takim razie napisać pętlę `for` i wyjść z niej, gdy `num >= 0`.

package main

import "fmt"

func main() {
	fmt.Println("Symulacja pętli do-while")
	num := 5
	for {
		fmt.Println("pętla działa...")
		if num >= 0 {
			break
		}
	}
}

💡 Zauważ, że warunki wykonania pętli gdy `num < 0` oraz przerwania pętli gdy `num >= 0` są równoważne.

Mimo że warunek `num > 0` jest początkowo fałszywy (num wynosi 5), ciało pętli wykona się raz, naśladując działanie pętli `do-while`.

//Wyjście
Symulacja pętli do-while
pętla działa...

Iteracja po tablicach przy użyciu pętli `for`

Podczas iteracji po tablicach w Golang za pomocą pętli `for` i klauzuli `range`, możemy uzyskać dostęp zarówno do indeksów, jak i elementów tablicy. Działa to podobnie do funkcji `enumerate` w Pythonie.

Tworzymy tablicę `numArray` liczb całkowitych, a następnie iterujemy po niej za pomocą pętli `for`:

package main

import "fmt"

func main() {
	fmt.Println("Iteracja po tablicy")
	numArray := []int{3, 7, 0, 10, 8, 9}
	for idx, num := range numArray {
		fmt.Println("Na indeksie", idx, ": ", num)
	}
}

Jak widać, możemy jednocześnie uzyskać dostęp do indeksu i elementu na danym indeksie:

//Wyjście
Iteracja po tablicy
Na indeksie 0 :  3
Na indeksie 1 :  7
Na indeksie 2 :  0
Na indeksie 3 :  10
Na indeksie 4 :  8
Na indeksie 5 :  9

Użycie `defer` w pętli `for` w Golang

W Golang słowo kluczowe `defer` służy do opóźnienia wykonania funkcji.

Jest ono często używane do zwalniania zasobów i obsługi błędów, ale warto też zrozumieć, jak używać `defer` wewnątrz pętli `for`. Sprawdźmy, co się stanie, gdy użyjemy `defer` w pętli `for` do opóźnienia wywołań funkcji `Println()`.

package main

import "fmt"

func main() {
	fmt.Println("Pętla for:")
	num := 5
	for i := 1; i <= num; i++ {
		defer fmt.Println(i)
	}
}

💬 Kiedy wywołanie funkcji jest opóźnione za pomocą `defer`, wywołanie to trafia na stos, a jego wykonanie następuje w kolejności LIFO (ostatnie weszło, pierwsze wyszło). Wywołanie następuje dopiero po zakończeniu działania funkcji, w której użyto `defer`.

Zatem `fmt.Println(5)` zostanie wykonane jako pierwsze, a `fmt.Println(1)` jako ostatnie:

//Wyjście
Pętla for:
5
4
3
2
1

Podsumowanie

Oto podsumowanie najważniejszych informacji, których nauczyłeś się w tym poradniku:

  • W Golang pętle `for` tworzy się za pomocą składni: `for inicjalizacja; warunek; aktualizacja { //ciało pętli }`.
  • Przepływ sterowania w pętli `for` jest dość prosty: zmienna sterująca jest inicjalizowana tylko raz, warunek decyduje o tym, czy wykonać ciało pętli, a aktualizacja odnosi się do modyfikacji zmiennej sterującej po każdej iteracji.
  • Zakres zmiennej pętli jest ograniczony do ciała pętli i nie można do niej uzyskać dostępu poza pętlą.
  • Mimo że Golang udostępnia tylko konstrukcję pętli `for`, możemy symulować zachowanie pętli `while` i `do-while` za jej pomocą.
  • Pętla `for` znajduje zastosowanie również podczas iteracji po tablicach oraz w połączeniu z opóźnieniem wywołań funkcji (`defer`).

Teraz zapoznaj się z pętlami `for` w Pythonie. Miłej nauki!🎉