Funkcje w Go - Dziennik pokładowy #3

Kamil Powroźnik

2019-06-06T17:03+02:00 | 5 min

Funkcje to najlepszy i najprostszy sposób na tworzenie re-używalnych bloków kodu. Bez nich, programowanie byłoby katorgą. Wyobraźmy sobie, że za każdym razem, gdy chcielibyśmy wykonać bardziej złożone operacje musielibyśmy powtarzać te same czynności. Damn it!

Jak się domyślasz go również pozwala wydzielać nam bloki kodu do takiej postaci. Funkcja w go może, przyjmować argumenty, jednak nic nie stoi na przeszkodzie, aby ich nie było. Spójrz jak może wyglądać prosta funkcja w tym języku.

func sum(x int, y int) int {
	return x + y
}

Jak możesz zauważyć funkcję zdefiniowałem za pomocą słowa kluczowego func, następnie podałem jej nazwę oraz w nawiasach argumenty wraz z ich typami, które będą mi potrzebne do wykonania obliczeń. Zaraz po nich podałem jeszcze typ zwracanej wartości.

Wywołajmy teraz tę funkcję i sprawdźmy czy otrzymaliśmy oczekiwany wynik:

func main() {
	result := sum(2, 5)
	fmt.Println(result)
	//output: 7
}

Super, funkcja działa tak jak zakładałem. Wróćmy jeszcze jednak do jej deklaracji.

Jeśli w funkcji podajemy argumenty tego samego typu, ich zapis możemy wtedy skrócić do postaci

func sum(x, y int) int {
	return x + y
}

Funkcja nie zawsze musi zwracać nam jeden wynik. Powyższą funkcję przerobiłem tak, aby zwracała sumę, oraz wynik z mnożenia przez siebie dwóch argumentów. Kod wygląda tak, a teraz poświęć chwilę jego analizie:

func sumAndMultiply(x int, y int) (int, int) {
	return x + y, x * y
}

func main() {
	sum, pow := sum(2, 5)
	fmt.Println(sum, pow)
	//output: 7, 10
}

W go wartości, które mają zostać zwrócone, mogą być nazwane, zobacz jak to zrobiłem:

func sumAndMultiply(x, y int) (sum, pow int) {
	sum = x + y
	pow = x * y
	return
}

Jednakże takie zwracanie wartości powinno być używane jedynie w prostych funkcjach. W przeciwnym wypadku czytelność kodu może zostać przysłonięta.

Rekurencja

Funkcje w go mogą również być wywoływane przez same siebie, dzięki czemu mamy do czynienia z rekurencją.

Oto prosty przykład (często spotykany na rozmowie kwalifikacyjnej) wykorzystania rekurencji do obliczania silni.

func factorial(x int) int {
if x > 1 {
	return x * factorial(x-1)
}
return 1
}

func main() {
	res := factorial(3)
	fmt.Println(res)
	//output: 6
}

Możesz zauważyć, że w momencie spełnienia odpowiedniego warunku (w tym przypadku x > 1) funkcja wywołuje samą siebie, w celu osiągnięcia przypadku podstawowego - x == 1.

Rekurencja bardzo często wykorzystywana jest podczas pisania algorytmów, oraz w językach funkcyjnych, gdzie nie istnieją pętle.

Domknięcia

Kolejną ważną cechą funkcji są domknięcia. Czym tak naprawdę są domknięcia? Mówiąc w skrócie domknięciem nazywamy efekt, w którym funkcja pamięta środowisko gdzie została zdefiniowana. W poniższym przypadku zwracam funkcję, która wykorzystuje utworzoną lokalnie zmienną i - zobacz:

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

func main() {

nextInt := getNum()

fmt.Println(nextInt()) // 1
fmt.Println(nextInt()) // 2
fmt.Println(nextInt()) // 3
fmt.Println(nextInt()) // 4

}

W ciele funkcji getNum() zwróciłem funkcję anonimową, która zawiera zmienną i ze zdefiniowaną wartością 0. Następnym krokiem było zwrócenie funkcji, która stworzyła domknięcie dla zmiennej i.

Tym oto sposobem dobrnęliśmy do końca mojego posta o funkcjach. W następnym artykule z tej serii przyjrzymy się interfejsom - czym są i do czego służą. Do usłyszenia!


    Hej, ta strona wykorzysuje pliki cookies, localStorage, sessionStorage oraz dane osobowe do celów analitycznych.