KW36: Gültigkeitsbereich von Variablen, Funktionen und Callbacks
Sobald wir von Funktionen (d.h. Unterprogrammen) sprechen, müssen wir uns über den Gültigkeitsbereich von Variablen Gedanken machen. Im Grundsatz gilt:
Variablen sind in dem Block sichtbar und verwendbar, indem sie definiert wurden.
Zu Beginn hier noch ein Easter Egg. Führe dazu folgendes scheinbar nutzlose Programm aus.
- EasterEgg
import this
- Weshalb Holländer?
Funktionen
Aus der Mathematik ist der Funktionsbegriff bekannt. In der Informatik kennen wir das gleiche Prinzip.
# Definition def meineErsteFunktion(): # Hier steht der Code # meiner ersten Funktion! print 42 # Begin Hauptprogramm for i in range(17): meineErsteFunktion()
Im obigen Programm wird zuerst eine Funktion mit dem Namen meineErsteFunktion definiert, welche die Zahl 42 ausgibt. Im Hauptprogramm wird die Funktion 17 mal aufgerufen. Soweit nichts spezielles. I.d.R. übergeben wir Funktionen mindestens einen Parameter.
# Definition def quadratZahl(x): print(x*x) # Hauptprogramm for i in range(1,11): quadratZahl(i)
Im Hauptprogramm wird die Funktion quadratZahl aufgerufen und der Wert der Variablen $i$ wird übergeben. In der Funktion quadratZahl übernimmt die lokale Variable $x$ den Wert von $i$. $x$ und $i$ sind zwei verschiedene Variablen mit dem gleichen Inhalt. Sinnvollerweise wird die Quadratzahl wieder zurückgegeben.
# Definition def quadratZahl(x): return(x*x) # Hauptprogramm for i in range(1,11): qZ=quadratZahl(i) print(qZ) # oder in einer Zeile print(quadratZahl(i))
Die Funktion quadratZahl gibt den Wert $x^2$ mit der Anweisung return(x*x) zurück an die Variable qZ im Hauptprogramm. Sinnvollerweise sind die Variablenbezeichnungen in der Funktion und im Hauptprogramm unterschiedlich.
Weitere Informationen findest du unter w3schools-funktionen
Gültigkeit von Variablen
Löse folgende Aufgaben durch.
Aufgabe 1 zu Variablen
- Wie lautet die Ausgabe von…
def f(): print(s) s="Python" f()
- und von …
def f(): print(s) s="Anaconda" print(s) s="Python" f()
- und von …
def f(): global s print(s) s="Anaconda" print(s) s="Python" f() print(s)
Aufgabe 2 Gegeben ist folgender Programmrumpf.
- heronWurzel
from gturtle import * def heronWurzel(r): # r = Radikand # w = Wurzel (Breite) # h = Hoehe # # Hier sollte dein Code stehen return(w) print(heronWurzel(inputInt("Gib eine natürliche Zahl ein:")))
Ergänze die Funktion heronWurzel mit dem Heronverfahren. Wurzelberechnung nach Heron hat die Grundidee ein Rechteck mit den Seitenlängen $1$ und $r$ (r für Radikand) in ein flächengleiches Quadrat zu verwandeln. Die Seitenlänge des Quadrates entspricht dann der Wurzel des Flächeninhalts. Wir beginnen mit einem Rechteck mit der Breite $r$ und der Höhe $1$. Damit ist die erste Näherung der Wurzel $w_1=r$. Die nächste Näherung ist der Mittelwert aus Grundlinie und Höhe, d.h. $w_2=\frac{1+r}{2}=\frac{h_1+w_1}{2}$ und damit die Höhe $h_2=r/w_2$.
Allgemein gilt: $$w_{n+1}=\frac{w_n+\frac{r}{w_n}}{2}$$ Sobald $|w_i -h_i|<\varepsilon$ ist bricht der Algorithmus ab. D.h. $\varepsilon$ ist die Genauigkeit.
Ergänze die Funktion heronWurzel so, dass bei jedem Iterationsvorgang das entsprechende Rechteck gezeichnet wird.
Die Kardioide von Nicholas
Aufgabe 3
- Studiert das nachfolgende Programm von Nicholas. Die Implementierung orientiert sich stark an der mathematischen Herleitung.
- Welches Konstrukt kennst du noch nicht?
- nicholasKardioide
from gpanel import * from math import sin,cos,pi R=1 r=1 points = [] def v1(a): return [(R+r)*cos(a),(R+r)*sin(a)] def v2(a): b = a * R/r - pi + a return [r*cos(b),r*sin(b)] def add(v1,v2): return [v1[0]+v2[0],v1[1]+v2[1]] makeGPanel(-4,4,-4,4) t=0 dt=0.0025 while t<=2*pi: p1=v1(t) p2=v2(t) p3=add(p1,p2) points.append(p3) move(0,0) setColor("black") delay(2) clear() circle(R) move(p1) circle(r) setColor("red") move(p3) fillCircle(0.1) for p in points: point(p) t=t+dt
Callbacks
Um die Zoom Funktion in unserem Programm “Mandelbrot” implementieren zu können, müssen wir die Mausevents verstehen.
Bearbeite dazu das Kapitel 3.7 von Tigerjython.
Ein Callback ist somit eine Verzweigung des Programmes aufgrund eines äusseren Ereignisses. Diese äusseren Ereignisse sind von uns Usern initiiert, indem wir mit der Mause oder mit der Tastatur eine Eingabe vornehmen. Aufgrund eines Ereignisses $E$ springt das Programm in ein dafür vorgesehenes Unterprogramm $UP$ führt dieses aus und kehrt wieder an die alte Stelle im Programm zurück um dort weiterzufahren.
Alle Ereignisse $E$, auf die wir reagieren wollen, müssen wir bei der Initialisierung des GPanel's angeben.
makeGPanel(mousePressed = onMousePressed, mouseReleased = onMouseReleased)
mousePressed und mouseReleased sind dabei die Ereignisse, onMousePressed und onMouseReleased sind die Namen der Unterprogramme die beim Eintreten des Ereignisses ausgeführt werden sollen. Eine Liste der Ereignisse findet ihr in der Dokumentation von Tigerjython.
Aufgabe 4
- Studiere das nachfolgende Programm
- Was macht das Programm? Bist du dir nicht sicher, so lade es herunter und probiere es aus.
- Callback
from gpanel import * def onMousePressed(x,y): clear() move(x,y) text("Eine Mausetaste wurde gedrückt") def onMouseReleased(x,y): clear() move(x,y) text("Die Mausetaste wurde wieder losgelassen") def onMouseDragged(x,y): clear() move(x,y) text("Die Mause bewegt sich (ev.) mit gedrückter Taste") makeGPanel(mousePressed = onMousePressed, mouseReleased = onMouseReleased, mouseDragged = onMouseDragged)
Aufgabe 5
- Ergänze dein Mandelbrot Programm so, dass du mit der Mause einen Zoombereich festlegen kannst. In einer ersten Version musst du den Zoombereich nicht mit einem Rechteck oder Quadrat anzeigen.
- Ergänze deine erste Version Mandelbrot mit Zoom so, dass der Zoobereich mit einem farbigen Rechteck oder Quadrat angezeigt wird. Benutze dabei die Idee aus Tigerjython bei den Gummibandlinien.
Hier eine mögliche Lösung.
- Mandelbrot-Zoom
from gpanel import * def onMousePressed(x, y): global xmin, ymin storeGraphics() xmin=x ymin=y def onMouseReleased(x, y): global xmax,ymax global neuesBild xmax=x ymax=ymin+(xmax-xmin) neuesBild=0 def onMouseDragged(x, y): global xmin,ymin delta=x-xmin recallGraphics() move(xmin,ymin) setColor("white") line(xmin, ymin, xmin+delta, ymin) line(xmin+delta, ymin, xmin+delta, ymin+delta) line(xmin+delta, ymin+delta, xmin, ymin+delta) line(xmin, ymin+delta, xmin, ymin) def getIterationColor(it): color = makeColor((it**2) % 256, (3*it) % 256, (5*it) % 256) return color def Mandelbrot(c): z=0 it=0 while it<maxIterations and abs(z)<R : z = z**2 + c it=it+1 if abs(z) > R: return it return maxIterations def drawP(): global xmin, xmax, xstep, xSize global ymin, ymax, ystep, ySize xstep=(xmax-xmin)/xSize ystep=(ymax-ymin)/ySize makeGPanel(Size(xSize,ySize), mousePressed = onMousePressed, mouseReleased = onMouseReleased, mouseDragged = onMouseDragged) window(xmin, xmax, ymin, ymax) y = ymin while y <= ymax: x = xmin while x <= xmax: c = complex(x,y) itCount = Mandelbrot(c) if itCount == maxIterations: setColor("black") else: setColor(getIterationColor(itCount)) point(c) x += xstep repaint() y += ystep #--------------------------------------- # Hauptprogramm #--------------------------------------- R = 2 maxIterations = 100 xmin = -2 xmax = 1 ymin = -1.5 ymax = 1.5 xSize=600 ySize=600 neuesBild=0 key = 0 while key!=27: if neuesBild==0: drawP(); neuesBild=1 key = getKeyCode()