sainth.de

sainth.de

Ein persönlicher Blog über alles, was mich interessiert, also überwiegend Programmierung im Allgemeinen und Spieleprogrammierung im Speziellen.

Ein anderer Denkansatz

Funktionale Programmierung erfordert einen anderen Denkansatz

Tobias Wink

Lesezeit: 3 Minuten

Different

Leider ist schon wieder einige Zeit seit dem letzten Post vergangen. Im letzten Post habe ich etwas über die Historie von funktionaler und imperativer Programmierung geschrieben. Dieser hier soll sich nun mit dem, im Vergleich zur imperativen Programmierung, anderen Denkansatz beschäftigen. Die hier gezeigten Quellcode-Beispiele beruhen dabei auf Java für die imperativen Beispiele bzw. auf Haskell für die funktionalen Beispiele. Ich werde bei den gezeigten Beispielen versuchen nur im Sprachstandard vorhandene Funktionen nutzen und nicht auf Bibliotheksfunktionen zurückgreifen.

Bei der imperativen Programmierung beschreibt man detailliert die Schritte, die vom Computer ausgeführt werden müssen, um die Aufgabe zu erfüllen. Im Gegensatz dazu wird bei der funktionalen Programmierung nicht beschrieben WIE etwas berechnet werden soll, sondern WAS. Nicht umsonst gehören funktionale Programmiersprachen zu den deklarativen Programmiersprachen.

Durch die Funktionen wird, genauso wie in der Mathematik, die “Welt” im Ist-Zustand dargestellt bzw. definiert. Generell ist die funktionale Programmierung mit ihren Begrifflichkeiten und Konzepten sehr stark an der Mathematik angelehnt. Das macht es nicht matheaffinen Menschen nicht gerade leicht, birgt aber den großen Vorteil, dass man, wenn man es geschafft hat die Konzepte zu verstehen, sich auf den mathematischen Hintergrund dieser Konzepte verlassen kann. Dadurch kann dann die Richtigkeit eines Programms mit “relativ” wenig Aufwand bewiesen werden, zumindest im Vergleich zu imperativen Programmen.

Während die meisten imperativen Algorithmen auf der Manipulation von Speicherbereichen (gerne als Variablen bezeichnet) beruhen, lassen sich die funktionalen Algorithmen durch Termersetzung umsetzen. Um diesen Unterschied zu verdeutlichen, soll das folgende Beispiel zur Berechnung der Summe von 1 bis n dienen.

Sehen wir uns zuerst das imperative Beispiel an:

int berechneSummeEinsBis(int n) {
  int ergebnis = 0;
  for (int zaehler = 1; zaehler <= n; zaehler++) {
    ergebnis += zaehler;
  }
  return ergebnis;
}

Bei der Ausführung der oberen Funktion werden während jedes Schleifendurchgangs die Variablen ergebnis und zaehler immer wieder verändert. Für n=3 sieht das dann so aus:

-- Initialisierung
ergebnis := 0
zaehler := 1
-- 1. Schleifendurchgang
ergebnis := 1
zaehler := 2
-- 2. Schleifendurchgang
ergebnis := 3
zaehler := 3
-- 3. Schleifendurchgang
ergebnis := 6
zaehler := 4

Im Vergleich würde eine Funktion für diese Berechnung folgendermaßen aussehen:

berechneSummeEinsBis :: Int -> Int
berechneSummeEinsBis 0 = 0
berechneSummeEinsBis n = n + berechneSummeEinsBis (n-1)

Bei der Ausführung von Haskell wird nun Termersetzung angewendet und erst am Schluss wird etwas berechnet:

berechneSummeEinsBis 3
-- Anwendung von berechneSummeEinsBis
3 + berechneSummeEinsBis (3-1)
-- Anwendung von (-)
3 + berechneSummeEinsBis 2
-- Anwendung von berechneSummeEinsBis
3 + 2 + berechneSummeEinsBis (2-1)
-- Anwendung von (-)
3 + 2 + berechneSummeEinsBis 1
-- Anwendung von berechneSummeEinsBis
3 + 2 + 1 + berechneSummeEinsBis (1-1)
-- Anwendung von (-)
3 + 2 + 1 + berechneSummeEinsBis 0
-- Anwendung von berechneSummeEinsBis
3 + 2 + 1 + 0
-- Anwendung von (+)
5 + 1 + 0
-- Anwendung von (+)
6 + 0
-- Anwendung von (+)
6

Man kann schon bei diesem einfachen Beispiel die unterschiedlichen Ansätze erkennen. Im imperativen Beispiel wird eine Schleife benutzt und im funktionalen Beispiel Rekursion. Selbstverständlich ist Rekursion auch in imperativen Sprachen möglich, aber unüblich, da es natürlich gewisse Gefahren birgt, die auch in Haskell nicht verschwinden. Aber das ist ein eigenes Thema, welches ich hoffentlich irgendwann nochmal aufgreifen werde.

Doch man kann auch die unterschiedliche Art der Interpretation erkennen. Während in der imperativen Sprache immer wieder die Variablen verändert werden, wird bei der funktionalen Sprache erst die gesamte Formel durch Termersetzung aufgebaut, um sie anschließend zu berechnen. Man könnte auch sagen, dass die funktionale Vorgehensweise der manuellen Vorgehensweise entspricht. Die Termersetzung ist nämlich das, was wir auch manuell beim Lösen einer Gleichung machen.

Neueste Artikel

Kategorien

Über Mich

Ich bin nur ein einfacher Mensch, der sich etwas mit Computern beschäftigt. 😉