16. Funktionen#

Der Schlüssel zur Berechnung von Lösungen bzw. der Verarbeitung von Information ist die Wiederholung. Wo es uns Schleifen erlauben eine bestimmte Folge von Arbeitsschritte lokal mehrfach auszuführen, erlauben es uns Funktionen eine Folge von Arbeitsschritte global auszuführen. In beiden Fällen ändern wir den Verlauf der Codeausführung, sodass dieser nicht mehr der Befehlsfolge (gelesen von oben nach unten) entspricht.

Funktionen bündeln eine Folge von Arbeitsschritten / Befehle. Es können Parameter definiert werden, sodass wir der Funktion Argumente als Eingabe übergeben. Wir sind im Stande dieses Bündel irgendwo in unserem Code auszuführen (ohne es noch einmal niederzuschreiben). Wird die Funktion im Code aufgerufen, springen wir, bzw. die CPU durch ihren Befehlszähler, an die Stelle der Funktion, das Bündel an Anweisungen wird ausgeführt und schlussendlich springen wir wieder an jene Stelle zurück, von der wir hergekommen sind.

Durch wiederholtes und verschachteltes Aufrufen einer Funktion erzeugen wir eine Art von Wiederholung. Zum Beispiel:

def successor(n):
  return n + 1

successor(successor(successor(1)))
4

Ruft sich eine Funktion, bis zu einer bestimmten Abbruchbedingung selbst auf, so sprechen wir von einer sog. Rekursion oder rekursiven Funktion. Zum Beispiel:

def fib(n):
  if n == 0:
    return 0
  if n == 1:
    return 1
  return fib(n-1) + fib(n-2)

fib(13)
233

Viele sog. built-in (eingebaute) Python-Funktionen haben wir bereits verwendet. Sie werden uns mit der Python-Standard Bibliothek mitgeliefert. Zum Beispiel ist type() oder auch len() eine solche Funktion.

type(len)
builtin_function_or_method

roboworld ist beispielsweise ein Modul, d.h. eine Ansammlung von Funktionalität, welches wir nutzten können. Deutlich bekannter ist das Modul numpy, welches für numerische Berechnungen verwendet wird. Um eine Funktion eines Moduls aufzurufen stellen wir den Modulnamen, z.B. numpy und einen Punkt . vorne an. Zuvor müssen wir das Modul geladen haben:

import numpy
numpy.linspace(0, 1, 100)
array([0.        , 0.01010101, 0.02020202, 0.03030303, 0.04040404,
       0.05050505, 0.06060606, 0.07070707, 0.08080808, 0.09090909,
       0.1010101 , 0.11111111, 0.12121212, 0.13131313, 0.14141414,
       0.15151515, 0.16161616, 0.17171717, 0.18181818, 0.19191919,
       0.2020202 , 0.21212121, 0.22222222, 0.23232323, 0.24242424,
       0.25252525, 0.26262626, 0.27272727, 0.28282828, 0.29292929,
       0.3030303 , 0.31313131, 0.32323232, 0.33333333, 0.34343434,
       0.35353535, 0.36363636, 0.37373737, 0.38383838, 0.39393939,
       0.4040404 , 0.41414141, 0.42424242, 0.43434343, 0.44444444,
       0.45454545, 0.46464646, 0.47474747, 0.48484848, 0.49494949,
       0.50505051, 0.51515152, 0.52525253, 0.53535354, 0.54545455,
       0.55555556, 0.56565657, 0.57575758, 0.58585859, 0.5959596 ,
       0.60606061, 0.61616162, 0.62626263, 0.63636364, 0.64646465,
       0.65656566, 0.66666667, 0.67676768, 0.68686869, 0.6969697 ,
       0.70707071, 0.71717172, 0.72727273, 0.73737374, 0.74747475,
       0.75757576, 0.76767677, 0.77777778, 0.78787879, 0.7979798 ,
       0.80808081, 0.81818182, 0.82828283, 0.83838384, 0.84848485,
       0.85858586, 0.86868687, 0.87878788, 0.88888889, 0.8989899 ,
       0.90909091, 0.91919192, 0.92929293, 0.93939394, 0.94949495,
       0.95959596, 0.96969697, 0.97979798, 0.98989899, 1.        ])
type(numpy.linspace)
numpy._ArrayFunctionDispatcher

Funktionen sind ein Mittel um Codewiederholungen zu verhindern und auch ein Mittel um Code zu strukturieren und bestimmte Funktionalität zu kapseln. Stellen Sie sich vor wir müssten jedes Mal wenn wir etwas ausgeben wollen den Code der Funktion print() niederschreiben. Schnell würden unsere Programme lange und auch langweilig und unübersichtlich werden.

Gute Funktionen zu schreiben kann sehr befriedigend für uns Computational Thinker*innen sein. Wir lösen damit oft ein Teilproblem und kommen der gesamten Lösung näher. Mit ein wenig Erfahrung können wir sogar Probleme lösen indem wir davon ausgehen, ein Teilproblem hätten wir bereits gelöst – auch wenn dies noch nicht der Fall ist.

Wie ist das gemeint? Nun, wir wollen zum Beispiel eine Funktion schreiben, welche uns die ersten n Primzahlen berechnet und in eine Liste packt. Wir gehen einfach davon aus es gäbe eine Funktion is_prime(k) die prüft ob k eine Primzahl ist oder nicht. Unter dieser Annahme schreiben wir unsere Funktion prime_list(n):

def is_prime(k):
  pass

def prime_list(n):
  primelist = []
  k = 1
  while len(primelist) < n:
    if is_prime(k):
      primelist.append(k)
    k += 1
  return primelist

Nachdem wir uns um die Generierung der Liste gekümmert haben, widmen wir uns der Funktion is_prime(k). Oder andere Entwickler*innen, die sich besser mit dem Problem auskennen, lösen es.

def is_prime(k):
  # a really stupid prime check
  if k == 1:
    return False
  elif k == 2:
    return True
  else:
    for i in range(2,k-1,1):
      if k % i == 0:
        return False
  return True

def prime_list(n):
  primelist = []
  k = 1
  while len(primelist) < n:
    if is_prime(k):
      primelist.append(k)
    k += 1
  return primelist

prime_list(20)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71]