Functions

Syntax

Functions

Das Konzept einer Funktion ist eines der wichtigsten in der Mathematik. Eine übliche Verwendung von Funktionen in Computersprachen ist die Implementierung mathematischer Funktionen. Eine solche Funktion berechnet ein oder mehrere Ergebnisse, die vollständig durch die an sie übergebenen Parameter bestimmt werden.

Das ist Mathematik, aber wir sprechen über Programmierung und Python. Was ist also eine Funktion in der Programmierung? Im allgemeinsten Sinne ist eine Funktion ein Strukturierungselement in Programmiersprachen, um eine Reihe von Anweisungen zu gruppieren, damit sie in einem Programm mehr als einmal verwendet werden können. Die einzige Möglichkeit, dies ohne Funktionen zu erreichen, besteht darin, Code durch Kopieren und Anpassen an verschiedene Kontexte wiederzuverwenden, was eine schlechte Idee wäre. Redundanter Code - in diesem Fall sich wiederholender Code - sollte vermieden werden! Die Verwendung von Funktionen verbessert normalerweise die Verständlichkeit und Qualität eines Programms. Dies senkt auch die Kosten für die Entwicklung und Wartung der Software.

Funktionen sind unter verschiedenen Namen in Programmiersprachen bekannt, z. als Unterprogramme, Routinen, Prozeduren, Methoden oder Unterprogramme.

Eine Funktion in Python wird durch eine def Anweisung definiert. Die allgemeine Syntax sieht folgendermaßen aus:

def Funktionsname (Parameterliste):
    Anweisungen, d. h. der Funktionskörper

Die Parameterliste besteht aus keinem oder mehreren Parametern. Parameter werden als Argumente bezeichnet, wenn die Funktion aufgerufen wird. Der Funktionskörper besteht aus eingerückten Anweisungen. Der Funktionskörper wird bei jedem Aufruf der Funktion ausgeführt. Wir zeigen dies im folgenden Bild:

Function Call: Control Flow

Der Code aus dem Bild ist im Folgenden zu sehen:

def f(x, y):
    z = 2 * (x + y)
    return z
print("Programm fängt an!")
a = 3
res1 = f(a, 2+a)
print("Ergebnis des Funktionsaufrufs: ", res1)
a = 4
b = 7
res2 = f(a, b)
print("Ergebnis des Funktionsaufrufs: ", res2)
Programm fängt an!
Ergebnis des Funktionsaufrufs:  16
Ergebnis des Funktionsaufrufs:  22

Wir rufen die Funktion zweimal im Programm auf. Die Funktion hat zwei Parameter, die x und y heißen. Dies bedeutet, dass die Funktion f zwei Werte erwartet, oder ich sollte" zwei Objekte "sagen. Zunächst rufen wir diese Funktion mit f (a, 2 + a) auf. Dies bedeutet, dass a zu x geht und das Ergebnis von 2 + a (5) 'zu' der Variablen y geht. Der Mechanismus zum Zuweisen von Argumenten zu Parametern wird als Argumentübergabe bezeichnet. Wenn wir die Anweisung return erreichen, wird das von z referenzierte Objekt zurückgegeben, was bedeutet, dass es der Variablen res1 zugewiesen wird. Nach Verlassen der Funktion f werden die Variable z und die Parameter x und y automatisch gelöscht.

Function Call: Argument Passing Part1

Die Verweise auf die Objekte sind im nächsten Diagramm zu sehen:

Function Call: Argument Passing Part2

Der nächste Python-Codeblock enthält ein Beispiel für eine Funktion ohne return-Anweisung. Wir verwenden die pass Anweisung innerhalb dieser Funktion. pass ist eine Nulloperation. Dies bedeutet, dass bei der Ausführung nichts passiert. Es ist nützlich als Platzhalter in Situationen, in denen eine Anweisung syntaktisch erforderlich ist, aber kein Code ausgeführt werden muss:

def doNothing():
     pass
</ pre>

Eine nützlichere Funktion:

def fahrenheit(T_in_celsius):
    """ Gibt die Temperatur T_in_celsius in Grad Fahrenheit zurück """
    return (T_in_celsius * 9 / 5) + 32
for t in (22.6, 25.8, 27.3, 29.8):
    print(t, ": ", fahrenheit(t))
22.6 :  72.68
25.8 :  78.44
27.3 :  81.14
29.8 :  85.64

""" Gibt die Temperatur in Grad Fahrenheit zurück """ ist der sogenannte Docstring. Er wird bei der help-Funktion verwendet:

help(fahrenheit)
Help on function fahrenheit in module __main__:
fahrenheit(T_in_celsius)
    Gibt die Temperatur in Grad Fahrenheit zurück

Der Docstring hängt an dem Attribut __doc__:

fahrenheit.__doc__
Führt man obigen Code aus, erhält man Folgendes:
' Gibt die Temperatur in Grad Fahrenheit zurück '

Standardargumente in Python

Wenn wir eine Python-Funktion definieren, können wir einen Standardwert für einen Parameter festlegen. Wenn die Funktion ohne das Argument aufgerufen wird, wird dieser Standardwert dem Parameter zugewiesen. Dies macht einen Parameter optional. Mit anderen Worten: Standardparameter sind Parameter, die beim Aufruf der Funktion nicht angegeben werden müssen. In diesem Fall werden die Standardwerte verwendet.

Wir werden das Funktionsprinzip von Standardparametern anhand eines einfachen Beispiels demonstrieren. Die folgende Funktion Hallo - die nicht sehr nützlich ist - begrüßt eine Person. Wenn kein Name angegeben wird, werden alle begrüßt:

def hallo(name="zusammen"):
    """ Grüßt eine Person """
    print("Hallo " + name + "!")
hallo("Peter")
hallo()
Hallo Peter!
Hallo zusammen!
def hello(name="Bruce"):
    """ Grüßt eine Person """
    print("Hello " + name + "!")
hello("Peter")
hello()
Hello Peter!
Hello Bruce!

Die Standardeinstellung für Standardeinstellungen

Im vorherigen Abschnitt haben wir die Standardparameter kennengelernt. Standardparameter sind recht einfach, aber Programmierer, die Python noch nicht kennen, stoßen häufig auf eine schreckliche und völlig unerwartete Überraschung. Diese Überraschung ergibt sich aus der Art und Weise, wie Python die Standardargumente und die Auswirkungen veränderbarer Objekte behandelt.

Veränderbare Objekte können nach der Erstellung geändert werden. In Python sind Wörterbücher Beispiele für veränderbare Objekte. Das Übergeben von veränderlichen Listen oder Wörterbüchern als Standardargumente an eine Funktion kann unvorhergesehene Auswirkungen haben. Programmierer, die Listen oder Wörterbücher als Standardargumente für eine Funktion verwenden, erwarten, dass das Programm bei jedem Aufruf der Funktion eine neue Liste oder ein neues Wörterbuch erstellt. Dies ist jedoch nicht das, was tatsächlich passiert. Standardwerte werden beim Aufruf einer Funktion nicht erstellt. Standardwerte werden genau einmal erstellt, wenn die Funktion definiert ist, d. H. Zur Kompilierungszeit.

Schauen wir uns die folgende Python-Funktion "Spammer" an, mit der eine "Tasche" voller Spam erstellt werden kann:

def spammer(tüte=[]):
    tüte.append("spam")
    return tüte

Wenn Sie diese Funktion einmal ohne Argument aufrufen, wird das erwartete Ergebnis zurückgegeben:

spammer()
Der obige Python-Code liefert Folgendes:
['spam']

Die Überraschung zeigt, wenn wir die Funktion ohne Argument erneut aufrufen:

spammer()
Wir können die folgenden Ergebnisse erwarten, wenn wir den obigen Python-Code ausführen:
['spam', 'spam']

Die meisten Programmierer haben das gleiche Ergebnis wie beim ersten Aufruf erwartet, d. H. "[" Spam "]" "

Um zu verstehen, was los ist, müssen Sie wissen, was passiert, wenn die Funktion definiert wird. Der Compiler erstellt ein Attribut __defaults__:

def spammer(tüte=[]):
    tüte.append("spam")
    return tüte
spammer.__defaults__
Der obige Python-Code liefert folgendes Ergebnis:
([],)

Wann immer wir die Funktion aufrufen, wird der Parameter bag dem Listenobjekt zugewiesen, auf das Spammer .__ Defaults __ [0] verweist:

for i in range(5):
    print(spammer())
    
print("spammer.__defaults__", spammer.__defaults__)
['spam']
['spam', 'spam']
['spam', 'spam', 'spam']
['spam', 'spam', 'spam', 'spam']
['spam', 'spam', 'spam', 'spam', 'spam']
spammer.__defaults__ (['spam', 'spam', 'spam', 'spam', 'spam'],)

Jetzt wissen und verstehen Sie, was los ist, aber Sie fragen sich vielleicht, wie Sie dieses Problem lösen können. Die Lösung besteht darin, den unveränderlichen Wert None als Standard zu verwenden. Auf diese Weise kann die Funktion bag dynamisch (zur Laufzeit) auf eine leere Liste setzen:

def spammer(tüte=None):
    if tüte is None:
        tüte = []
    tüte.append("spam")
    return tüte
for i in range(5):
    print(spammer())
    
print("spammer.__defaults__", spammer.__defaults__)
['spam']
['spam']
['spam']
['spam']
['spam']
spammer.__defaults__ (None,)

Docstring

Die erste Anweisung im Hauptteil einer Funktion ist normalerweise eine Zeichenfolgenanweisung namens Docstring, auf die mit dem Funktionsname .__ doc__ zugegriffen werden kann. Zum Beispiel:

def hallo(name="zusammen"):
    """ Grüßt einer Person """
    print("Hallo " + name + "!")
print("Die Docstring der Funktion Hallo:" + hallo.__doc__)
Die Docstring der Funktion Hallo: Grüßt einer Person 

Schlüsselwortparameter

Die Verwendung von Schlüsselwortparametern ist eine alternative Möglichkeit, Funktionsaufrufe durchzuführen. Die Definition der Funktion ändert sich nicht. Ein Beispiel:

def sumsub(a, b, c=0, d=0):
    return a - b + c - d
print(sumsub(12, 4))
print(sumsub(42, 15, d=10))
8
17

Schlüsselwortparameter können nur solche sein, die nicht als Positionsargumente verwendet werden. Wir können den Nutzen im Beispiel sehen. Wenn wir keine Schlüsselwortparameter gehabt hätten, hätte der zweite Aufruf der Funktion alle vier Argumente benötigt, obwohl das Argument c nur den Standardwert benötigt:

print(sumsub(42,15,0,10))
17

Rückgabewerte

In unseren vorherigen Beispielen haben wir eine return-Anweisung in der Funktion sumsub verwendet, jedoch nicht in Hello. Wir können also sehen, dass eine return-Anweisung nicht zwingend erforderlich ist. Aber was wird zurückgegeben, wenn wir nicht explizit eine return-Anweisung geben. Mal schauen:

def no_return(x, y):
    c = x + y
res = no_return(4, 5)
print(res)
None

Wenn wir dieses kleine Skript starten, wird None gedruckt, d. H. Der spezielle Wert None wird von einer Funktion ohne Rückgabe zurückgegeben. None wird auch zurückgegeben, wenn wir nur eine Rückgabe in einer Funktion ohne Ausdruck haben:

def empty_return(x, y):
    c = x + y
    return
res = empty_return(4, 5)
print(res)
None

Andernfalls wird der Wert des Ausdrucks nach der Rückgabe zurückgegeben. Im nächsten Beispiel wird 9 gedruckt:

def return_sum(x, y):
    c = x + y
    return c
res = return_sum(4, 5)
print(res)
9

Fassen wir dieses Verhalten zusammen: Funktionskörper können eine oder mehrere return-Anweisungen enthalten. Sie können sich überall im Funktionskörper befinden. Eine return-Anweisung beendet die Ausführung des Funktionsaufrufs und "gibt" das Ergebnis, d. H. Den Wert des Ausdrucks nach dem Schlüsselwort return, an den Aufrufer zurück. Wenn die return-Anweisung keinen Ausdruck enthält, wird der spezielle Wert None zurückgegeben. Wenn der Funktionscode keine return-Anweisung enthält, endet die Funktion, wenn der Kontrollfluss das Ende des Funktionskörpers erreicht und der Wert None zurückgegeben wird.

Mehrere Werte zurückgeben

Eine Funktion kann genau einen Wert zurückgeben, oder wir sollten besser ein Objekt sagen. Ein Objekt kann ein numerischer Wert sein, z. B. eine Ganzzahl oder ein Gleitkommawert. Es kann aber auch z.B. eine Liste oder ein Wörterbuch. Wenn wir also beispielsweise 3 Ganzzahlwerte zurückgeben müssen, können wir eine Liste oder ein Tupel mit diesen drei Ganzzahlwerten zurückgeben. Das heißt, wir können indirekt mehrere Werte zurückgeben. Das folgende Beispiel, das die Fibonacci-Grenze für eine positive Zahl berechnet, gibt ein 2-Tupel zurück. Das erste Element ist die größte Fibonacci-Zahl kleiner als x und die zweite Komponente ist die kleinste Fibonacci-Zahl größer als x. Der Rückgabewert wird sofort durch Auspacken in die Variablen lub und sup gespeichert:

def fib_intervall(x):
    """ gibt die größten Fibonacci zurück
     Zahl kleiner als x und die niedrigste
     Fibonacci-Zahl höher als x"""
    if x < 0:
        return -1
    (alt,neu) = (0,1)
    while True:
        if neu < x:
            (alt,neu) = (neu,alt+neu)
        else:
            if neu == x: 
                neu = alt+neu
            return (alt, neu)
            
while True:
    x = int(input("Ihre Nummer: "))
    if x <= 0:
        break
    (lub, sup) = fib_intervall(x)
    print("Größte Fibonacci-Zahl kleiner als x: " + str(lub))
    print("Kleinste Fibonacci-Zahl größer als x: " + str(sup))
Ihre Nummer: 5
Größte Fibonacci-Zahl kleiner als x: 3
Kleinste Fibonacci-Zahl größer als x: 8
Ihre Nummer: 8
Größte Fibonacci-Zahl kleiner als x: 5
Kleinste Fibonacci-Zahl größer als x: 13
Ihre Nummer: 90
Größte Fibonacci-Zahl kleiner als x: 89
Kleinste Fibonacci-Zahl größer als x: 144
Ihre Nummer: -22

Lokale und globale Variablen in Funktionen

Variablennamen sind standardmäßig lokal für die Funktion, in der sie definiert werden.

def f(): 
    print(s)
s = "Python"
f()
Python
def f(): 
    s = "Perl"
    print(s)
f()
Perl
s = "Python"
f()
print(s)
Perl
Python
def f(): 
    print(s)
    s = "Perl"
    print(s)
s = "Python" 
f()
print(s)
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-59-81b2fbbc4d42> in <module>
      6 
      7 s = "Python"
----> 8 f()
      9 print(s)
<ipython-input-59-81b2fbbc4d42> in f()
      1 def f():
----> 2     print(s)
      3     s = "Perl"
      4     print(s)
      5 
UnboundLocalError: local variable 's' referenced before assignment
s = "Python" 
f()
print(s)
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-60-6661890debe5> in <module>
      1 s = "Python"
----> 2 f()
      3 print(s)
<ipython-input-59-81b2fbbc4d42> in f()
      1 def f():
----> 2     print(s)
      3     s = "Perl"
      4     print(s)
      5 
UnboundLocalError: local variable 's' referenced before assignment

Wenn wir das vorherige Skript ausführen, erhalten wir die Fehlermeldung: UnboundLocalError: Lokale Variable 's', auf die vor der Zuweisung verwiesen wird.

Die Variable s ist in f () mehrdeutig, d. H. Beim ersten Druck in f () könnte das globale s mit dem Wert "Python" verwendet werden. Danach definieren wir eine lokale Variable s mit der Zuordnung s = "Perl".

def f():
    global s
    print(s)
    s = "Hund"
    print(s) 
s = "Katze" 
f()
print(s)
Katze
Hund
Hund

Wir haben die Variable innerhalb des Skripts global gemacht. Daher wird alles, was wir innerhalb des Funktionskörpers von f mit s tun, mit der globalen Variablen s außerhalb von f gemacht.

Beliebige Anzahl von Parametern

Es gibt viele Situationen in der Programmierung, in denen die genaue Anzahl der erforderlichen Parameter nicht a priori bestimmt werden kann. In Python kann eine beliebige Parameternummer mit sogenannten Tupelreferenzen erreicht werden. Ein Sternchen "*" wird vor dem letzten Parameternamen verwendet, um ihn als Tupelreferenz zu kennzeichnen. Dieses Sternchen sollte nicht mit der C-Syntax verwechselt werden, bei der diese Notation mit Zeigern verbunden ist. Beispiel:

def arithmetisches_mittel(erste, *werte):
    """ Diese Funktion berechnet das arithmetische Mittel eines nicht leeren
         beliebige Anzahl von Zahlenwerten """
    return (erste + sum(werte)) / (1 + len(werte))
print(arithmetisches_mittel(45,32,89,78))
print(arithmetisches_mittel(8989.8,78787.78,3453,78778.73))
print(arithmetisches_mittel(45,32))
print(arithmetisches_mittel(45))
61.0
42502.3275
38.5
45.0

Das ist großartig, aber wir haben immer noch ein Problem. Möglicherweise haben Sie eine Liste mit numerischen Werten. Wie zum Beispiel,

 x = [3, 5, 9] 

Sie können es nicht mit anrufen

 
arithmetisches_mittel (x) 

weil "arithmetisches_mittel" mit einer Liste nicht fertig wird. Ich nenne es mit

 
arithmetisches_mittel (x [0], x [1], x [2]) 

ist umständlich und vor allem innerhalb eines Programms unmöglich, da die Liste beliebig lang sein kann.

Die Lösung ist einfach. Wir fügen einen Stern vor dem x hinzu, wenn wir die Funktion aufrufen.

 
arithmetisches_mittel (* x) 

Dadurch wird die Liste "entpackt" oder vereinzelt.

Ein praktisches Beispiel: Wir haben eine Liste von 4, 2-Tupel-Elementen:

 meine_liste = [('a', 232), 
           ('b', 343), 
           ('c', 543), 
           ('d', 23)] 

Wir möchten diese Liste in die folgende Liste mit 2 Elementen und 4 Tupeln umwandeln:

 [('a', 'b', 'c', 'd'), 
 (232, 343, 543, 23)] 

Dies kann mithilfe des * -Operators und der Zip-Funktion folgendermaßen erfolgen:

 Liste (zip (* meine_liste)) </ pre>

Beliebige Anzahl von Schlüsselwortparametern

Im vorherigen Kapitel haben wir gezeigt, wie eine beliebige Anzahl von Positionsparametern an eine Funktion übergeben wird. Es ist auch möglich, eine beliebige Anzahl von Schlüsselwortparametern an eine Funktion als Wörterbuch zu übergeben. Zu diesem Zweck müssen wir das doppelte Sternchen "**" verwenden.

def f(**kwargs):
    print(kwargs)
f()
{}
f(de="Deutsch",en="Englisch",fr="Franzözisch")
{'de': 'Deutsch', 'en': 'Englisch', 'fr': 'Franzözisch'}

Ein Anwendungsfall ist der folgende:

def f(a, b, x, y):
    print(a, b, x, y)
d = {'a':'append', 'b':'block','x':'extract','y':'yes'}
f(**d)
append block extract yes

Übungen mit Funktionen

Übung 1

Schreiben Sie eine Funktion, die einen Text aufnimmt und mit einer Caesar-Chiffre verschlüsselt. Dies ist eine der einfachsten und am häufigsten bekannten Verschlüsselungstechniken. Jeder Buchstabe im Text wird durch einen Buchstaben ersetzt, der eine feste Anzahl von Stellen weiter im Alphabet enthält.

Was ist mit dem Entschlüsseln des codierten Textes?

Die Caesar-Chiffre ist eine Substitutions-Chiffre.

caesar cipher

Übung 2

Wir können eine weitere Substitutions-Chiffre erstellen, indem wir den Propheten permutieren und die Buchstaben dem entsprechenden permutierten Alphabet zuordnen.

Schreiben Sie eine Funktion, die einen Text und ein Wörterbuch benötigt, um den angegebenen Text mit einem permutierten Alphabet zu entschlüsseln oder zu verschlüsseln.

Übung 3

Schreiben Sie eine Funktion txt2morse, die einen Text in Morsecode übersetzt, d. H. Die Funktion gibt eine Zeichenfolge mit dem Morsecode zurück.

Schreiben Sie eine weitere Funktion morse2txt, die eine Zeichenfolge im Morsecode in eine „normale“ Zeichenfolge übersetzt.

Die Morsezeichen sind durch Leerzeichen getrennt. Wörter mit drei Leerzeichen.

International Morse Code

Übung 4

Vielleicht ist der erste Algorithmus, der zur Approximation von $ \ sqrt {S} $ verwendet wird, als "babylonische Methode" bekannt, benannt nach den Babyloniern, oder "Heldenmethode", benannt nach dem griechischen Mathematiker Hero of Alexandria aus dem ersten Jahrhundert, der den ersten expliziten gab Beschreibung der Methode.

Wenn eine Zahl $ x_n $ nahe an der Quadratwurzel von $ a $ liegt, dann $$ x_ {n + 1} = \ frac {1} {2} (x_n + \ frac {a} {x_n}) $$ wird eine bessere Annäherung sein.

Schreiben Sie ein Programm, um die Quadratwurzel einer Zahl nach der babylonischen Methode zu berechnen.

Übung 5

Schreiben Sie eine Funktion, die die Position des n-ten Auftretens von a berechnet String Sub in einem anderen String s. Wenn sub in s nicht vorkommt, wird -1 zurückgegeben.

Lösungen

Lösung zu Übung 1

import string
abc = string.ascii_uppercase
def caesar(txt, n, coded=False):
    """ Gibt den codierten oder decodierten Text zurück """
    result = ""
    for char in txt.upper():
        if char not in abc:
            result += char
        elif coded:
            result += abc[(abc.find(char) + n) % len(abc)]
        else:
            result += abc[(abc.find(char) - n) % len(abc)]
    return result
n = 3
x = caesar("Hallo, hier bin ich!", n)
print(x)
print(caesar(x, n, True))
EXIIL, EFBO YFK FZE!
HALLO, HIER BIN ICH!

In der vorherigen Lösung ersetzen wir nur die Buchstaben. Jedes Sonderzeichen bleibt unberührt. Die folgende Lösung fügt einige Sonderzeichen hinzu, die ebenfalls permutiert werden. Spezielle Zeichen, die nicht in abc enthalten sind, gehen bei dieser Lösung verloren!

import string
abc = string.ascii_uppercase + " .,-?!"
def caesar(txt, n, coded=False):
    """ Gibt den codierten oder decodierten Text zurück """
    result = ""
    for char in txt.upper():
        if coded:
            result += abc[(abc.find(char) + n) % len(abc)]
        else:
            result += abc[(abc.find(char) - n) % len(abc)]
    return result
n = 3
x = caesar("Hallo, hier bin ich!", n)
print(x)
print(caesar(x, n, True))
E-IILZXEFBOX?FKXF!E,
HALLO, HIER BIN ICH!

Lösung zu Übung 2

import string
from random import sample
alphabet = string.ascii_letters
permutated_alphabet = sample(alphabet, len(alphabet))
encrypt_dict = dict(zip(alphabet, permutated_alphabet))
decrypt_dict = dict(zip(permutated_alphabet, alphabet))
def encrypt(text, edict):
    """ Every character of the text 'text'
    is mapped to the value of edict. Characters
    which are not keys of edict will not change"""
    res = ""
    for char in text:
        res = res + edict.get(char, char)
    return res
# Donald Trump: 5:19 PM, September 9 2014
txt = """Windmills are the greatest 
threat in the US to both bald 
and golden eagles. Media claims 
fictional ‘global warming’ is worse."""
ctext = encrypt(txt, encrypt_dict)
print(ctext + "\n")
print(encrypt(ctext, decrypt_dict))
mkGiMkFFs WAI KqI QAIWKIsK 
KqAIWK kG KqI bp KY wYKq wWFi 
WGi QYFiIG IWQFIs. xIikW gFWkMs 
akgKkYGWF ‘QFYwWF eWAMkGQ’ ks eYAsI.
Windmills are the greatest 
threat in the US to both bald 
and golden eagles. Media claims 
fictional ‘global warming’ is worse.

Lösung zu Übung 3

latin2morse_dict = {'A':'.-', 'B':'-...', 'C':'-.-.', 'D':'-..', 
                    'E':'.', 'F':'..-.', 'G':'--.','H':'....', 
                    'I':'..', 'J':'.---', 'K':'-.-', 'L':'.-..', 
                    'M':'--', 'N':'-.', 'O':'---', 'P':'.--.', 
                    'Q':'--.-', 'R':'.-.', 'S':'...', 'T':'-', 
                    'U':'..-', 'V':'...-', 'W':'.--', 'X':'-..-', 
                    'Y':'-.--', 'Z':'--..', '1':'.----', '2':'...--', 
                    '3':'...--', '4':'....-', '5':'.....', '6':'-....', 
                    '7':'--...', '8':'---..', '9':'----.', '0':'-----', 
                    ',':'--..--', '.':'.-.-.-', '?':'..--..', ';':'-.-.-', 
                    ':':'---...', '/':'-..-.', '-':'-....-', '\'':'.----.', 
                    '(':'-.--.-', ')':'-.--.-', '[':'-.--.-', ']':'-.--.-', 
                    '{':'-.--.-', '}':'-.--.-', '_':'..--.-'}
# reversing the dictionary:
morse2latin_dict = dict(zip(latin2morse_dict.values(),
                            latin2morse_dict.keys()))
print(morse2latin_dict)
{'.-': 'A', '-...': 'B', '-.-.': 'C', '-..': 'D', '.': 'E', '..-.': 'F', '--.': 'G', '....': 'H', '..': 'I', '.---': 'J', '-.-': 'K', '.-..': 'L', '--': 'M', '-.': 'N', '---': 'O', '.--.': 'P', '--.-': 'Q', '.-.': 'R', '...': 'S', '-': 'T', '..-': 'U', '...-': 'V', '.--': 'W', '-..-': 'X', '-.--': 'Y', '--..': 'Z', '.----': '1', '...--': '3', '....-': '4', '.....': '5', '-....': '6', '--...': '7', '---..': '8', '----.': '9', '-----': '0', '--..--': ',', '.-.-.-': '.', '..--..': '?', '-.-.-': ';', '---...': ':', '-..-.': '/', '-....-': '-', '.----.': "'", '-.--.-': '}', '..--.-': '_'}
def txt2morse(txt, alphabet):
    morse_code = ""
    for char in txt.upper():
        if char == " ":
            morse_code += "   "
        else:
            morse_code += alphabet[char] + " "
    return morse_code
def morse2txt(txt, alphabet):
    res = ""
    mwords = txt.split("   ")
    for mword in mwords:
        for mchar in mword.split():
            res += alphabet[mchar]
        res += " "
    return res
mstring = txt2morse("So was?", latin2morse_dict)
print(mstring)
print(morse2txt(mstring, morse2latin_dict))
... ---    .-- .- ... ..--.. 
SO WAS? 

Lösung zu Übung 4

def heron(a, eps=0.000000001):
    """ Approximate the square root of a"""
    previous = 0
    new = 1
    while abs(new - previous) > eps:
        previous = new
        new = (previous + a/previous) / 2
    return new
print(heron(2))
print(heron(2, 0.001))
1.414213562373095
1.4142135623746899

Lösung zu Übung 5

def findnth(s, sub, n):
    num = 0
    start = -1
    while num < n:
        start = s.find(sub, start+1)
        if start == -1: 
            break
        num += 1
    
    return start
s = "abc xyz abc jkjkjk abc lkjkjlkj abc jlj"
print(findnth(s,"abc", 3))
19