Funktionale Programmierung in Go: Prinzipien und Beispiele

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Funktionale Programmierung in Go: Vom Imperativen zum Deklarativen



Go (Golang) wird traditionell mit imperativer und Concurrent-Programmierung assoziiert, aber sein mächtiges Typsystem und die Unterstützung von Funktionen erster Klasse machen die Anwendung vieler Prinzipien der funktionalen Programmierung (FP) möglich. In diesem Artikel werden wir untersuchen, wie man FP-Idiome in Go anwendet, ohne Einbußen bei der Leistung und Lesbarkeit des Codes hinzunehmen.



Grundprinzipien der FP im Kontext von Go



Funktionale Programmierung in Go bedeutet nicht den vollständigen Verzicht auf veränderlichen Zustand. Stattdessen verwenden wir die folgenden Konzepte:



  • Reine Funktionen — Funktionen, deren Ergebnis nur von den Argumenten abhängt, ohne Seiteneffekte.
  • Unveränderlichkeit (Immutability) — Bevorzugung der Erstellung neuer Datenstrukturen anstelle der Änderung bestehender.
  • Funktionen höherer Ordnung — Funktionen, die andere Funktionen als Argumente akzeptieren oder sie zurückgeben.
  • Closures — Funktionen, die Variablen aus dem äußeren Gültigkeitsbereich einfangen.


Funktionen höherer Ordnung: map, filter, reduce



Go hat keine eingebauten Generics (bis Version 1.18), daher werden die klassischen map, filter, reduce über Interfaces oder explizites Kopieren implementiert. Betrachten wir ein Beispiel mit ganzen Zahlen:



package main

import "fmt"

// Map — wendet eine Funktion auf jedes Element eines Slice anfunc Map(s []int, fn func(int) int) []int { result := make([]int, len(s)) for i, v := range s { result[i] = fn(v) } return result}

// Filter — behält nur Elemente, die die Bedingung erfüllenfunc Filter(s []int, fn func(int) bool) []int { var result []int for _, v := range s { if fn(v) { result = append(result, v) } } return result}

// Reduce — faltet einen Slice zu einem einzelnen Wertfunc Reduce(s []int, init int, fn func(int, int) int) int { acc := init for _, v := range s { acc = fn(acc, v) } return acc}

func main() { nums := []int{1, 2, 3, 4, 5}

// Verdoppelt jedes Element doubled := Map(nums, func(x int) int { return x * 2 }) fmt.Println("Map:", doubled) // [2 4 6 8 10]

// Behält nur gerade Zahlen evens := Filter(nums, func(x int) bool { return x%2 == 0 }) fmt.Println("Filter:", evens) // [2 4]

// Summiert alle Elemente sum := Reduce(nums, 0, func(acc, x int) int { return acc + x }) fmt.Println("Reduce:", sum) // 15}


Wichtig: In Go 1.18+ mit Generics können universelle Versionen erstellt werden:



// Generic Mapfunc MapGeneric[T, U any](s []T, fn func(T) U) []U {    result := make([]U, len(s))    for i, v := range s {        result[i] = fn(v)    }    return result}


Closures und Currying in Go



Closures in Go sind ein mächtiges Werkzeug zum Erstellen von Funktionen mit Zustandserhaltung. Betrachten wir ein Beispiel eines Sequenzgenerators:



package main

import "fmt"

// Fibonacci-Sequenzgeneratorfunc fibonacci() func() int { a, b := 0, 1 return func() int { a, b = b, a+b return a }}

// Currying: eine Funktion, die Argumente schrittweise annimmtfunc add(a int) func(int) int { return func(b int) int { return a + b }}

func main() { fib := fibonacci() fmt.Println(fib()) // 1 fmt.Println(fib()) // 1 fmt.Println(fib()) // 2

// Currying add5 := add(5) fmt.Println(add5(10)) // 15 fmt.Println(add5(20)) // 25}


Closures sind besonders nützlich für:

  • Erstellung konfigurierbarer Funktionen (Middleware)
  • Implementierung des "Funktionsfabrik"-Musters
  • Organisation von Callback-Mechanismen


Unveränderlichkeit

Blogs

Buchempfehlungen