Go dla początkujących - Część 3: Funkcje

1/29/2025 Kurs Go

Mateusz Kędziora

image

Witaj w kolejnym poście z kursu Go dla początkujących! Dziś zajmiemy się jednym z najważniejszych elementów każdego języka programowania – funkcjami. Funkcje pozwalają nam organizować kod w logiczne bloki, co znacznie ułatwia jego czytanie, pisanie i utrzymanie. W Go funkcje są szczególnie potężne i elastyczne, oferując wiele możliwości, takich jak funkcje anonimowe i closure.

Czym są funkcje?

Funkcja to blok kodu, który wykonuje określone zadanie. Możemy przekazywać do funkcji dane (parametry), a funkcja może zwracać wynik (wartość zwracana). Funkcje pozwalają nam uniknąć powtarzania kodu i zwiększają czytelność programu.

Definiowanie funkcji w Go

Funkcję w Go definiujemy za pomocą słowa kluczowego func, po którym następuje nazwa funkcji, lista parametrów w nawiasach, typ wartości zwracanej (lub brak wartości, jeśli funkcja nic nie zwraca) oraz ciało funkcji w nawiasach klamrowych.

Oto przykład prostej funkcji, która dodaje dwie liczby całkowite:

func dodaj(a int, b int) int {
    return a + b
}

Wywoływanie funkcji

Aby wywołać funkcję, wystarczy podać jej nazwę i wartości argumentów w nawiasach:

wynik := dodaj(5, 3) // wynik będzie równy 8

Parametry funkcji

Funkcje mogą przyjmować dowolną liczbę parametrów różnych typów. Jeśli parametry są tego samego typu, możemy je zadeklarować grupowo:

func dodaj(a, b int) int {
    return a + b
}

Wartości zwracane

Funkcja może zwracać jedną lub więcej wartości. Jeśli funkcja zwraca więcej niż jedną wartość, typy tych wartości musimy podać w nawiasach po słowie kluczowym return:

func podziel(a, b int) (int, int) {
    iloraz := a / b
    reszta := a % b
    return iloraz, reszta
}

Funkcje anonimowe

W Go możemy tworzyć funkcje anonimowe, czyli funkcje bez nazwy. Funkcje anonimowe są często używane jako argumenty innych funkcji lub do tworzenia closure.

Oto przykład funkcji anonimowej, która wyświetla komunikat na ekranie:

func() {
    fmt.Println("Witaj, świecie!")
}() // Natychmiastowe wywołanie funkcji

Closure

Closure to funkcja, która “pamięta” wartości zmiennych z jej otoczenia, nawet po tym, jak funkcja zewnętrzna, w której została zdefiniowana, zakończyła swoje działanie. Closure są bardzo przydatne do tworzenia funkcji, które zachowują stan między wywołaniami.

Oto przykład closure, która zlicza, ile razy została wywołana:

licznik := func() int {
    i := 0
    return func() int {
        i++
        return i
    }
}()

fmt.Println(licznik()) // 1
fmt.Println(licznik()) // 2
fmt.Println(licznik()) // 3

Zastosowanie funkcji w praktyce

Funkcje są nieodzowne w programowaniu w Go. Pozwalają nam dzielić kod na mniejsze, łatwiejsze do zarządzania części. Dzięki funkcjom możemy tworzyć modularny i czytelny kod, który jest łatwiejszy do testowania i debugowania.

1. Funkcje jako argumenty innych funkcji (funkcje wyższego rzędu)

W Go funkcje mogą być przekazywane jako argumenty do innych funkcji. To pozwala na tworzenie bardziej elastycznego i uniwersalnego kodu.

package main

import "fmt"

// Funkcja, która przyjmuje inną funkcję jako argument
func wykonajOperacje(a, b int, operacja func(int, int) int) int {
    return operacja(a, b)
}

// Funkcje, które mogą być przekazane jako argument
func dodaj(a, b int) int {
    return a + b
}

func odejmij(a, b int) int {
    return a - b
}

func main() {
    wynikDodawania := wykonajOperacje(5, 3, dodaj) // Przekazujemy funkcję dodaj
    fmt.Println("Wynik dodawania:", wynikDodawania) // Wynik dodawania: 8

    wynikOdejmowania := wykonajOperacje(5, 3, odejmij) // Przekazujemy funkcję odejmij
    fmt.Println("Wynik odejmowania:", wynikOdejmowania) // Wynik odejmowania: 2
}

2. Funkcje zwracające funkcje (domknięcia)

Funkcje w Go mogą również zwracać inne funkcje. To pozwala na tworzenie domknięć (closures), czyli funkcji, które “pamiętają” wartości zmiennych z ich otoczenia, nawet po tym, jak funkcja zewnętrzna, w której zostały zdefiniowane, zakończyła swoje działanie.

package main

import "fmt"

// Funkcja, która zwraca inną funkcję (domknięcie)
func generatorLicznika() func() int {
    licznik := 0 // Zmienna lokalna dla funkcji generatorLicznika
    return func() int {
        licznik++
        return licznik
    }
}

func main() {
    licznik := generatorLicznika() // Przypisujemy domknięcie do zmiennej licznik

    fmt.Println(licznik()) // 1
    fmt.Println(licznik()) // 2
    fmt.Println(licznik()) // 3
}

3. Funkcje w strukturach

Funkcje mogą być również definiowane wewnątrz struktur. Takie funkcje nazywamy metodami. Metody działają na konkretnych instancjach struktur i mają dostęp do ich pól.

package main

import "fmt"

// Definiujemy strukturę Prostokat
type Prostokat struct {
    szerokosc  float64
    wysokosc float64
}

// Metoda, która oblicza pole prostokąta
func (p Prostokat) pole() float64 {
    return p.szerokosc * p.wysokosc
}

func main() {
    prostokat := Prostokat{szerokosc: 5, wysokosc: 3}
    fmt.Println("Pole prostokąta:", prostokat.pole()) // Pole prostokąta: 15
}

4. Funkcje w interfejsach

Interfejsy w Go definiują zbiór metod, które typ musi implementować, aby spełniał dany interfejs. Funkcje są więc nieodłącznie związane z interfejsami.

package main

import "fmt"

// Definiujemy interfejs Figura
type Figura interface {
    pole() float64
}

// Struktura Prostokat implementuje interfejs Figura
type Prostokat struct {
    szerokosc  float64
    wysokosc float64
}

func (p Prostokat) pole() float64 {
    return p.szerokosc * p.wysokosc
}

// Funkcja, która przyjmuje argument typu Figura
func wyswietlPole(f Figura) {
    fmt.Println("Pole figury:", f.pole())
}

func main() {
    prostokat := Prostokat{szerokosc: 5, wysokosc: 3}
    wyswietlPole(prostokat) // Pole figury: 15
}

5. Funkcje w pakietach

W Go funkcje są podstawowym elementem organizacji kodu w pakiety. Każdy pakiet może zawierać wiele funkcji, które mogą być używane przez inne pakiety.

// Pakiet math
package math

// Funkcja eksportowana (zaczyna się od dużej litery)
func Dodaj(a, b int) int {
    return a + b
}

// Funkcja nieeksportowana (zaczyna się od małej litery)
func odejmij(a, b int) int {
    return a - b
}

// W innym pakiecie
package main

import (
    "fmt"
    "mojaaplikacja/math" // Importujemy nasz pakiet math
)

func main() {
    wynik := math.Dodaj(5, 3) // Używamy funkcji z pakietu math
    fmt.Println("Wynik dodawania:", wynik) // Wynik dodawania: 8
}

Mam nadzieję, że te przykłady pomogą Ci lepiej zrozumieć, jak używać funkcji w języku Go. Zachęcam do eksperymentowania i tworzenia własnych funkcji, aby jeszcze lepiej opanować ten ważny element języka.

Praca domowa

  1. Napisz funkcję, która oblicza pole prostokąta.
  2. Napisz funkcję, która sprawdza, czy liczba jest parzysta, czy nieparzysta.
  3. Napisz funkcję, która zwraca największy element z listy liczb.
  4. Spróbuj napisać własny przykład użycia closure.

Podsumowanie

Funkcje to kluczowy element języka Go. Dzięki nim możemy pisać modularny, czytelny i łatwy w utrzymaniu kod. Zachęcam do eksperymentowania z funkcjami, pisania własnych przykładów i odkrywania ich możliwości.

Zapraszam do lektury kolejnych postów z kursu Go dla początkujących, w których omówimy kolejne ciekawe tematy.

Polecane artykuły