Repetition i Python 3 Exemplen fac Orginalet I Scheme använde vi rekursion för all slags repetition. Efterom Scheme är ett funktionellt språk återsänder alla språkkonstruktioner ett värde men i Python kan vi även (mellan-)lagra värden i variabler. Man kan alltså ha styrstrukturer som inte kräver rekursion för repetitiva beräkningar. Det är viktigt eftersom Python typiskt klarar 1000 rekursiva anrop. Man kan ställa om max rekursionsdjup men det slukar minne. Python har två sådana iterativa styrstrukturer och vi har redan sett dem. Det finns en mängd sätt att variera dem och jag kommer ge exempel på hur de används. Exemplen har vi redan sett, här fokuserar vi på variationerna i koden. (define fac (if (= n 0) 1 (* n (fac (- n 1)))))) Svansrekursiv (define fac (define fac-svans (lambda (m res) (if (> m n) res (fac-svans (+ m 1) (* res m))))) (fac-svans 2 1))) DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 1 / 23 DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 2 / 23 Exemplen fac i Python Exemplen fac motivering Direktkopia av den svansrekursiva Scheme-varianten def facsvans(m, res): if m > n: return facsvans(m + 1, res * m) return facsvans(2, 1) Betraktar man en trace på fac-svans eller gör en utskrift från motsvarande Python-program får man m res 2 1 3 2 4 6 5 24 6 120 Vi ser att resultatet erhålls genom att parametrarna m och res uppdateras för varje anrop och att det som lagrats i parametern res lämnas som resultat då m passerat värdet för n. Det räcker alltså att sätta initalvärden på res och m och uppdatera dem utan extra anrop. I Python kan man formulera detta direkt utan rekursion. DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 3 / 23 DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 4 / 23
Repetition i Python while Repetition i Python for... # variabler för mellanlagring m, res = 2, 1 # ersätter inre funk.param. while m <= n: # villkoret inverterat m, res = m+1, res * m; # uppdatera m och res # återsänd resultatet Jag utnyttjar att man kan sätta flera variabler samtidigt I while-satsen kollar man om villkoret är uppfyllt innan man gör något. Skulle villkoret inte vara uppfyllt då programmet stöter på while-satsen så utförs ingenting. Man måste, som vid rekursion, vara noga med att se till att man kommer till en situation där villkoret inte längre är uppfyllt. Man har ingen inbyggd analys till hjälp. Vet man hur många gånger eller kan låta datorn räkna ut hur många gånger man ska göra någonting kan man använda for res = 1 for m in range(2, n + 1): # upprepa för m = 2, 3, 4,..., n res = res * m res = 1 for m in range(n, 1, -1): # upprepa för m = n, n-1,..., 3, 2 res := res * m DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 5 / 23 DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 6 / 23 Repetition i Python for... Repetition i Python for... to/downto formellt for variabel in lista sekvens itererbar struktur: sats(-er) for i in [3, 27, hej ]... for i, j in [[1, 2], [3, 4]]: print(i, j) Går inte att iterera över nycklar och värden i ett lexikon (dictionary) Men det går att iterera över alla nycklar i ett dictionary d = { kalle :321529, lotta :7169293} for k in d: print(k, d[k]) for variabel in range(m, n): satser Om m n eller for variabel in range(m, n, -1): satser och m < n så utförs ingen sats. OBS! att den genererade sekvensen inkluderar m men inte n d.v.s. att range(m, n) ger värdena m, m+1,..., n-2, n-1 i första fallet och m, m-1,..., n+2, n+1 i andra fallet I range(m, n, s) måste både m, n och s (steget) vara heltal Repetition med de nu visade konstruktionerna kallas iteration DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 7 / 23 DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 8 / 23
Rekursion kontra iteration för- och nackdelar Iteration exempel 2 (fib) i Scheme Vid rekursion behöver man bara tänka på Hur vet man att man är klar? Vad gör man då? Hur tar man ett steg närmare lösningen? Vid iteration måste man tänka på allt samtidigt men Iteration är resurssnålt För att rekursion ska vara resurssnålt måste man använda ett språk som kan eliminera svansrekursion (som Scheme, ML, Haskell eller Miranda) och skriva svansrekursiva program (inte alltid enkelt) Rekursivt i Scheme: (define fib (if (< n 2) 1 (+ (fib (- n 1)) (fib (- n 2)))))) Svansrekursivt (define fib (define fib-inner (lambda (m f1 f2) (if (> m n) f2 (fib-inner (+ m 1) f2 (+ f1 f2))))) (fib-inner 1 0 1))) DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 9 / 23 DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 10 / 23 Iteration exempel 2 (fib) i Scheme... Iteration exempel 2 (fib) i Python Trace av svansrekursiva varianten > (fib 5) (fib-inner 1 0 1) (fib-inner 2 1 1) (fib-inner 3 1 2) (fib-inner 4 2 3) (fib-inner 5 3 5) (fib-inner 6 5 8) 8 8 def fib(n): def inner(m, f1, f2): if m > n: return f2 return inner(m + 1, f2, f1 + f2) return inner(1, 0, 1) DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 11 / 23 DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 12 / 23
Iteration exempel 2 (fib) med while och for) Iteration exempel: kolla om ett tal är primtal def fib(n): m, f1, f2 = 1, 0, 1 while m <= n: m, f1, f2 = m+1, f2, f1 + f2 return f2 def fib(n): f1, f2 = 0, 1 for m in range(1, n + 1): f1, f2 = f2, f1 + f2 return f2 import math def main(): while True: try: n = int(input( Mata in ett tal: )) except: p = sieve(n) if p[len(p) - 1] == n: print(n, är ett primtal ) print(n, är inte ett primtal ) # end while # end main DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 13 / 23 DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 14 / 23 Iteration exempel: kolla om ett tal är primtal... Iteration exempel: primtalsfaktorisering def sieve(n): # skapa en lista med tal [2..n] primes = list(range(2, n + 1)) # gå igenom listan, tal för tal upp till roten ur n for i in range(2, math.floor(math.sqrt(n)) + 1): # tag bort multipler av talen for j in primes: if j % i == 0 and j!= i: primes.remove(j) # kvar är bara primtalen return primes # end sieve main() import math def main(): while True: try: i = int(input( Mata in ett tal: )) except: factors = factorize(i) if len(factors) == 1: print (i, är ett primtal ) print (i, har faktorerna:, end= ) for j in factors: print (j, end= ) print() DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 15 / 23 DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 16 / 23
Iteration exempel: primtalsfaktorisering... Inmatning med felhantering tips: def factorize(n): f = list() k = 2 while k * k <= n: if n % k == 0: f.append(k) n = n // k k += 1 if n!= 1: f.append(n) return f main() def main(): while True: try: print( Mata in ett heltal, end= ) x = input( (avsluta med ctrl-d): ) n = int(x) except EOFError: print("bye!") except: print(" " + x + " är inte ett heltal! Bye!") # gör något vettigt med n do_something_with_n(n) DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 17 / 23 DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 18 / 23 Listor Listor... Vi har använt listor i stor utsträckning och i Python är listor effektiva datastrukturer med en mängd olika möjligheter. Vi ska tita lite på vad man kan göra med dem. L = list() skapa en tom lista L = list(range(1, 6)) skapa listan L = [1, 2, 3, 4, 5] L = list( abc ) skapa listan L = [ a, b, c ] L = [0] * 5 skapa listan L = [0, 0, 0, 0, 0] Vi kan loopa igenom en lista: L = list( Serafim ) for x in L: print (x, end = ) Skriver ut: S e r a f i m Vi kan använda index för samma sak: i = 0 while i < len(l): print (L[i], end= ) i += 1 eller: for i in range(len(l)): print (L[i], end= ) Alla uppräkningsbara datastrukturers längd kan räknas ut med len() Listor kan ändras, man kan lägga till element (L.append(objekt)), man kan ta bort element (L.remove(objekt)), man kan slå ihop listor (L += list( Dahl )), man kan uppdatera listelement (L[3] = o ). print(l[2:6]) ger då [ r, o, f, i ] DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 19 / 23 DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 20 / 23
Listor... I det sista exemplet använde jag vad man kallar slicing ( slice = dela, skiva) i Python-världen. Jag kan alltså ange från och med vilket index och fram till vilket index i listan som jag vill använda. Antag att L = [ S, e, r, a, f, i, m,, D, a, h, l ] första index är alltid 0 L[m:n] ger L från och med index = m till index n-1 L[2:6] blir alltså [ r, a, f, i ] L[:6] blir [ S, e, r, a, f, i ] alltså från början (index = 0) till och med index n - 1 L[8:] blir [ D, a, h, l ] alltså från och med index = 8 och till slutet L[-1] OBS! ett sätt att hitta listans sista element... Listor, fördefinierade funktioner Python använder i stor utsträckning funktioner som är bundna till typen. Det betyder att man oftast anropar funktionerna med punktnotation. Antar fortfarande att vi har en lista L. Funktioner: L.append (item) lägger till item sist i listan L L.index (item) hittar positionen för item. Det blir ett fel om item inte finns i listan L.insert (index, item) stoppar in item på platsen index i listan L. Alla element i L skiftar ett steg åt höger från och med position index. Skulle L inte vara lång nog läggs det nya elementet sist och om index är negativt läggs det nya elementet först L.sort () sorterar listan L. OBS! listan L blir sorterad, det genereras ingen ny, sorterad lista L.remove (item) tar bort item från listan L. Ger fel om item inte finns i listan L.reverse () vänder på listan L. Fungerar som om man skrivit: for i in range(len(l) // 2): L[i], L[-i] = L[-i], L[i] DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 21 / 23 DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 22 / 23 Satser och funktioner som fungerar på listor Python har en del möjligheter med sater och funktioner som inte är bundna till listor men fungerar på listor. del. En sats i Python som tillåter att man tar bort ett element baserat på dess index. Ex.: del L[3] Ger ett fel om listan är kortare än 4 element. min- och max-funktionerna tar (naturligtvis) ut min resp. max ur en datastruktur. För att kopiera en lista kan man inte skriva M = L (om L är en lista) eftersom man då sätter M att referera till samma lista som L. Det blir ingen kopia. M = list(l) fungerar likväl som M = [] + L DA2001 (Föreläsning 12) Datalogi 1 Hösten 2012 23 / 23