# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: percent
#       format_version: '1.3'
#       jupytext_version: 1.14.0
#   kernelspec:
#     display_name: Python [conda env:CompWS22] *
#     language: python
#     name: conda-env-CompWS22-py
# ---

# %% [markdown] slideshow={"slide_type": "slide"}
# # Schleifen

# %% [markdown]
# Schleifen sind Strukturelemente, die ein wiederholtes Durchlaufen von Programmblöcken ermöglichen. Man unterscheidet while-Schleifen und for-Schleifen.
#
# In der letzten Woche hatten wir gesehen, dass auch durch Verwendung eines rekursiven Funktionsaufrufs ein wiederholtes Abarbeiten von Anweisungen möglich ist. Für viele Anwendungen sind Schleifen aber der offensichtlichere Ansatz um Anweisungen wiederholt auszuführen. 

# %% [markdown]
# ## for-Schleifen
# Der Aufbau einer for Schleife ist wie folgt, wobei das __else__ optional ist
# ```python
#     for element in iterierbarer_Liste:
#         block von anweisungen
#     [else]
#         block von anweisungen
# ```

# %% slideshow={"slide_type": "fragment"}
for i in [1,2,4]:
    print(i)

# %% slideshow={"slide_type": "fragment"}
for i in (1, 2, 3):
    print(i)

# %% [markdown]
# das geht einfacher mit dem Range Objekt. Damit erhält man alle ganzen Zahlen in einem Bereich.
# ```python 
# range([anfang],ende)
# ```
# Wir der Anfangsindex nicht angegeben, beginnt der Bereich bei 0. Der Endeindex ist wieder exklusiv.

# %% slideshow={"slide_type": "fragment"}
for i in range(1, 4):
    print(i)

# %%
list(range(3))

# %% [markdown]
# Auch Wörterbücher, Mengen und Zeichenketten sind iterierbar

# %% slideshow={"slide_type": "slide"}
wb = {1 : 'a', 2 : 'b', (1, 2) : 'a+b', 0 : 'Null'}
for k in wb: 
    print(k, wb[k])

# %% slideshow={"slide_type": "fragment"}
menge = {1, 3, 4, 1, 2}
for element in menge:
    print(element)

# %%
for zeichen in '+-+-':
    print(zeichen)

# %% [markdown]
# __print__ beendet jede Ausgabe mit einer neuer Zeile. Mit dem _end_ Paramater kann man das Ende der Ausgabe festlegen.

# %%
for zeichen in '+-+-':
    print(zeichen, end=',')

# %% [markdown]
# Das optionale __else__ in einer for Schleife wird selten verwendet. Es ist vermutlich nur nützlich, wenn man mit einem __break__ eine Schleife vorzeitig abbricht.

# %%
for n in range(2, 10):
    for x in range(2, n):
        if n % x == 0: # x teilt n
            print(f'{n} ist {x} * {int(n/x)}')
            break
    else: # die Schleife mit x wurde nicht abgebrochen oder die Schleife ist leer
        print(f'{n} ist prim')

# %% [markdown]
# Es kommt etwas anderes raus, wenn wir das __else__ "verschieben"

# %%
for n in range(2, 10):
    for x in range(2, n):
        if n % x == 0: # x teilt n
            print(f'{n} ist {x} * {int(n/x)}')
            break
        else: # hier gehört das else zum if, alsoe falls x kein Teiler von n ist
            print(f'{n} ist prim')

# %% [markdown]
# oder weglassen

# %%
for n in range(2, 10):
    for x in range(2, n):
        if n % x == 0: # x teilt n
            print(f'{n} ist {x} * {int(n/x)}')
            break
        print(f'{n} ist prim')

# %%
for x in {}:
    print(42)
else:
    print(1)
    

# %% [markdown]
# hier ist das __else__ nutzlos

# %% [markdown]
# ### Nützliche built-in Funktionen
# Built-in Funktionen, die das Programmieren von __for__-Schleifen vereinfachen, sind
# __zip()__, __enumerate()__, __reversed()__, __sorted()__.
#
#
# __zip()__ verbindet mehrere Sequenzen und erlaubt ein gemeinsames Iterieren. 

# %%
str1='AEIOU'
lst1=['Affe','Esel','Igel','Ohrenkneifer','Uhu','Zebra']

for i, j in zip(str1, lst1):
    print(f'{i} wie {j}')

# %% [markdown]
# __reversed()__ dreht die Reihenfolge um. 

# %%
for i in reversed(lst1):
    print(i)

# %% [markdown]
# __enumerate()__ zählt die durchiterierten Elemente ab

# %%
for i, j in enumerate(lst1):
    print(i, j)

# %%
for i,j in enumerate(lst1, start=1): # Man kann auch bei start zu zählen beginnen
    print(i, j)

# %% [markdown] slideshow={"slide_type": "slide"}
# ## while Schleife

# %% [markdown]
# Der Aufbau einer while Schleife ist wie folgt, wobei das __else__ optional ist
# ```python
#     while bedingung:
#         anweisung
#     [else]
#         anweisung
# ```
# Mit __break__ kann man die Schleife abbrechen. 

# %%
a = 1
while a <= 10:
    a = a + (a+1)
    print(a)
a

# %% slideshow={"slide_type": "fragment"}
a = 1
while True:
    if a > 10:
        break
    a = a + (a+1)
    print(a)
a


# %%
def arek(a):
    print(a)
    if a > 10:
        return a
    return arek(2*a+1)
a = arek(1)
a

# %% slideshow={"slide_type": "fragment"}
from numpy.random import randint

# %% slideshow={"slide_type": "fragment"}
zaehler = 0
summe = 0
while summe < 100:
    summe += 1+randint(6)  # += inplace plus (summe = summe + 1 + randint(6))
    zaehler += 1
print(f'Nach {zaehler} mal würfeln ist die Augensumme {summe}')

# %% [markdown] slideshow={"slide_type": "slide"}
# ## Listen-Abstraktion
# ### für Listen (list comprehension)

# %% slideshow={"slide_type": "fragment"}
a = [[1]]
for _ in range(5):
    a.append([1])
a

# %% slideshow={"slide_type": "fragment"}
aa = [[1] for _ in range(5)]
aa

# %% [markdown]
# ### für Wörterbücher

# %% slideshow={"slide_type": "fragment"}
{i : i**2 for i in range(5)}

# %% [markdown]
# ### für Mengen (set comprehension)

# %%
{i**2 for i in range(5)}

# %% [markdown]
# man kann dabei auch Bedingungen abfragen

# %%
{i**2 for i in range(5) if (i**2)%2 > 0} # alle ungeraden Quadratzahlen bis 16

# %% [markdown]
# Primzahlen bis N

# %%
N = 20
[n for n in range(2, N) if all([n%m > 0 for m in range(2, n)])]


# %% [markdown] slideshow={"slide_type": "slide"}
# ## Ein Beispiel: Minimum

# %% slideshow={"slide_type": "fragment"}
def mymin(a, b):
    """ Berechnet das Minimum der Eingabeparameter"""
    if a<b:
        return a
    else:
        return b


# %% slideshow={"slide_type": "fragment"}
mymin(3.2, 2.1)


# %% [markdown]
# Der __\*__ dient hier dazu beliebig viele Eingabeparameter in einen Tupel zusammenfassen

# %% slideshow={"slide_type": "slide"}
def mymin(*eingabewerte):
    """ Berechnet das Minimum der Betraege der Eingabeparameter"""
    #print(eingabewerte, type(eingabewerte))
    minimum = abs(eingabewerte[0])
    for a in eingabewerte:
        
        assert isinstance(a, (float, int, complex)), 'Eingabewerte müssen Zahlen sein'
        if abs(a) < minimum:
            minimum = abs(a)
            
    return minimum



# %% slideshow={"slide_type": "fragment"}
mymin(2, 2.1, 3, 1+2j)

# %%
liste = [2 , 3 , 4, 1]

# %% [markdown]
# Der * dient hier dazu die Liste zu entpacken

# %%
mymin(*liste)

# %%
mymin(liste)

# %%
