Imperativ och Funktionell Programmering i Python #TDDD73 Fredrik Heintz, IDA fredrik.heintz@liu.se @FredrikHeintz
Översikt Repetition: Satser och uttryck Variabler, datatyper, synlighet och skuggning Upprepning, iteration och rekursion
Två viktiga byggstenar Satser gör något. print("hello") x = x + 3 Uttryck har ett värde. a + b/2 square(y + 7)
Information som programmet använder x = 35 i en variabel i molnet Hur ser informationen ut? zaza4 <14> cat data.txt Kalle 14 p Lisa 23 p Achmed 19 p Pierre 21 p i en fil SQL Server i en databas
Datatyper En datatyp är en väldefinierad sorts information som ett datorprogram kan använda. Datatypen talar om vilka dataobjekt som ingår i typen (vilken domän som typen har) samt vilka operationer som är tillåtna. Datatypen svarar alltså på frågorna: Hur ser objekten ut? Vad kan man göra med dem? De flesta programspråk har ett antal inbyggda datatyper (som ofta är ganska lika) samt möjligheter för programmeraren att definiera egna typer.
Några datatyper i Python enkla sammansatta Datatyp Domän Exempel på objekt int Heltal 4711 float Flyttal 3.14 str Strängar 'spam' list Listor [1, 2, 75, 6, 7] test av datatyp >>> type(2.78) <class 'float'> >>> type(5) == int True >>> isinstance([1,2,3],list) True
Variabler deklararas inte I en del andra programspråk behöver man deklarera variabler, d.v.s. ange vilken datatyp de ska ha. Det behöver vi inte i Python. En variabel har egentligen ingen datatyp, utan är bara en platshållare. Det är innehållet som har en datatyp och en variabel kan ha innehåll av många olika typer under sin livstid. >>> a = 4711 >>> b = ['spam', 'spam'] >>> a = b >>> print(a) ['spam', 'spam']
Synlighet Vi har redan märkt att en funktions parametrar och lokala variabler existerar enbart så länge funktionen körs. Mer allmänt kan man tala om en symbols synlighet eller räckvidd och menar då den del av koden där symbolen existerar och kan användas. Detta gäller både variabler och funktioner. På engelska benämns begreppet scope eller ibland (scope of) visibility. Python använder, som många andra språk, lexikalisk räckvidd (eng. lexical scoping) ibland även kallat statisk räckvidd, vilket innebär att en symbols synlighet kan avgöras enbart genom att titta på källkoden.
Exempel på olika nivåer av synlighet a = 1 def yttre(b): c = 3 def inre(d): e = 5 return a+b+c+d+e return inre(4) Synlighet för symbolerna a, yttre Synlighet för symbolerna b, c, inre Synlighet för symbolerna d, e print(yttre(2))
Synlighet Python har fyra nivåer av synlighet, d.v.s. när Python behöver veta vilket värde en symbol har finns det fyra nivåer att söka igenom. Minnesreglen för att komma ihåg dessa är LEGB. L (Local) innebär att man söker igenom den aktuella funktionen, dess parametrar och lokala variabler. E (Enclosing) innebär att man söker igenom funktioner som finns en eller flera nivåer utanför den aktuella funktionen. G (Global) innebär att man tittar efter symbolen på toppnivå i den aktuella modulen/filen. B (Builtin) innebär att man söker igenom de inbyggda symbolerna i Python. Om symbolen inte återfinns på någon av nivåerna signalerar Python ett fel.
Exempel på olika nivåer av synlighet a = 1 def yttre(b): c = 3 def inre(d): e = 5 return a+b+c+d+e return inre(4) print(yttre(2)) def yttre(b): c = 3 def inre(d): e = 5 return a+b+c+d+e return inre(4) def inre(d): e = 5 return a+b+c+d+e a = 1 yttre = <function> b = 2 c = 3 inre = <function> d = 4 e = 5
Skuggning x = 100 def f(x): return x * x print(f(5)) def g(a): i = "Really important data!" for i in range(a): print("*") print("and now i is", i) g(3) 25 En lokal symbol skuggar en global symbol med samma namn och gör att den inte är åtkomlig i den inre synlighetsnivån. * * * And now i is 2 Observera att for inte öppnar någon ny nivå av synlighet, som i en del andra språk.
Upprepning
En sång för långa bussresor 99 bottles of beer on the wall, 99 bottles of beer. Take one down, pass it around. 98 bottles of beer. 98 bottles of beer on the wall, 98 bottles of beer. Take one down, pass it around. 97 bottles of beer. 97 bottles of beer on the wall, 97 bottles of beer. Take one down, pass it around. 96 bottles of beer. 96 bottles of beer on the wall, 96 bottles of beer. Take one down, pass it around. 95 bottles of beer. 95 bottles of beer on the wall, 95 bottles of beer. Take one down, pass it around. 94 bottles of beer. 94 bottles of beer on the wall, 94 bottles of beer. Take one down, pass it around. 93 bottles of beer. 93 bottles of beer on the wall, 93 bottles of beer. Take one down, pass it around. 92 bottles of beer.... Räkna ner talet i från 99 till 1 och gör följande varje gång: Skriv ut de två textraderna och peta in talet i på rätt ställe. for i in range(???): print(???)
Hur fungerar range()? >>> for i in range(4):... print(i)... 0 1 2 3 >>> list(range(4)) [0, 1, 2, 3] >>> list(range(1,4)) [1, 2, 3] >>> list(range(4,0,-1)) [4, 3, 2, 1] Tre olika former range(slut) range(start, slut) range(start, slut, steg)
Upprepning # Iteration def song_i(n): for i in range(n,0,-1): print(i, "bottles of beer on the wall,", i, "bottles of beer") print("take one down, pass it around,", i-1, "bottles of beer") # Rekursion def song_r(i): if i > 0: print(i, "bottles of beer on the wall,", i, "bottles of beer") print("take one down, pass it around,", i-1, "bottles of beer") song_r(i-1)
Olika matematiska definitioner av fakultet! = 1 2 1 Fakulteten av n är produkten av alla tal från 1 till n.
Fakultetsfunktionen i Python 1. Börja med att sätta resultatet till 1 2. Gå igenom alla tal från n ned till (men inte inklusive) 0 i steg om -1. >>> def factorial(n):... result = 1... for i in range(n,0,-1):... result = result * i... return result... >>> factorial(5) 120 4. Skicka tillbaka resultatet. 3. För varje sådant tal i, multiplicera på det på resultatet.
Olika matematiska definitioner av fakultet! = 1 2 1! = 1 = 1 1! > 1 Fakulteten av n är produkten av alla tal från 1 till n. Fakulteten av n är 1 om n är 1, annars är det n gånger fakulteten av n-1. Iteration Rekursion
Rekursiv definiton av fakultetsfunktionen! = 1 = 1 1! > 1 basfall rekursionsfall def factorial(n): if n == 1: return 1 else: return n * factorial(n 1)
Upprepning iteration eller rekursion? def factorial(n): result = 1 for i in range(n,0,-1): result = result * i return result def factorial(n): if n == 1: return 1 else: return n * factorial(n 1) Snabbare Naturlig, rakt på Långsammare, och använder mer minne Matematisk, elegant, läsbar, bakvänd Vilken lösning man bör välja styrs av vilken typ av data man har och vad man vill åstadkomma. Än så länge ska vi öva på båda metoderna.
Upprepning: Iteration och rekursion Först genomförde vi upprepningar över tal med iteration (konstruktionen for) och rekursion (funktioner som anropar sig själva). Nu ska vi titta på motsvarande metoder för att upprepa något för varje element i en lista. Hur kan man tänka? Iteration handlar om att hela tiden ha ett delresultat och uppdatera det varje gång man gör upprepningssteget. Rekursion handlar om att försöka omvandla det existerande problemet till ett lite mindre problem och starta om, till dess att problemet är så litet att det är trivialt. Vi ska nu gå igenom en funktion som beräknar längden av en lista på två olika sätt: iterativt och rekursivt.
Iterativ beräkning 0123 a b c def length(sequence): result = 0 for element in sequence: result = result + 1 return result I detta exempel beräknar vi längden av listan [ a, b, c ] med iterativ metod.
Iterativ beräkning a b c Hur lång är listan [ a, b, c ]? Först förbereder vi en räknare. 3 Sedan går vi igenom listan ett element i taget. Tack så mycket! 0123 I detta exempel beräknar vi längden av listan [ a, b, c ] med iterativ metod.
Rekursiv beräkning Testa om listan är tom med if not sequence 1 +? 1 a b def length(sequence): if not sequence: return 0 else: return 1 + length(sequence[1:]) +? c 1 + 0? [ ] Resten av listan motsvarar sequence[1:] I detta exempel beräknar vi längden av listan [ a, b, c ] med rekursiv metod.
Rekursiv beräkning 3 I detta exempel beräknar vi längden av listan [ a, b, c ] med rekursiv metod. Hur lång är listan [ a,...]? Tack så mycket! 1 + längden av resten Hur lång är listan [ b,...]? 2 1 + längden av resten Hur lång är listan [ c,...]? 1 + längden av resten 1 Hur lång är listan [ ]? 0
Mallar för upprepning över en talserie z = startvärde för beräkning (0 om vi kombinerar med +, 1 om vi kombinerar med *) op = operation för att kombinera ihop resultatet, t.ex. + eller * def iter(n): res = z for i in range(n): res = op(i,res) return res def rec(n): if n == 0: return z else: return op(n,rec(n 1)) Mallar för upprepning över en lista op = operation som ska utföras på varje element i listan def iter(seq): res = [] for elem in seq: res = res+[op(elem)] return res def rec(seq): if not seq: return [] else: return [op(seq[0])]+rec(seq[1:])
Utökade mallar för upprepning över en lista med villkor op1, op2 = operationer som ska utföras på varje element i listan cond = villkor som avgör om det är op1 eller op2 som ska användas def iter(seq): res = [] for elem in seq: if cond(elem): res = res+[op1(elem)] else: res = res+[op2(elem)] return res def rec(seq): if not seq: return [] elif cond(seq[0]): return [op1(seq[0])]+rec(seq[1:]) else: return [op2(seq[0])]+rec(seq[1:])
Exempel som använder den utökade mallen Vi vill ha en funktion count som kan räkna hur många gånger ett visst element förekommer i en lista. Exempel: >>> count([1,2,1,3,5],1) 2 def count(seq,goal): result = 0 for element in seq: if element == goal: result = result + 1 return result def count(seq,goal): if not seq: return 0 elif seq[0] == goal: return 1 + count(seq[1:],goal) else: return count(seq[1:],goal)
Dubbelrekursion över listor Vi har nu skrivit funktioner som med hjälp av iteration eller rekursion bearbetat alla element i en sträng eller en lista. Listor är mer generella än strängar och kan innehålla i princip vad som helst, t.ex. andra listor. Om vi vill behandla alla element i den typen av mer komplicerade strukturer är det i princip enbart rekursion som gäller, om man ska lösa det generellt. Vi kallar det för dubbelrekursion när vi bearebetar listor i listor. >>> s1 = [1, 'two', 3, 4] >>> s2 = [1, 'two', [3, 4, 'five'], [True]] >>> len(s2) 4 >>> type(s2[2]) <class 'list'>
Dubbelrekursion 1 two True def len2(seq): if not seq: return 0 elif isinstance(seq[0], list): return len2(seq[0]) + len2(seq[1:]) else: return 1 + len2(seq[1:]) 3 4 five Hur beräknar man den totala längden när man har listor i listor? s2 = [1, 'two', [3, 4, 'five'], [True]]
Spårning: Vad händer? a: len2([1, 'two', [3, 4, 'five'], [True]]) b: len2(['two', [3, 4, 'five'], [True]]) c: len2([[3, 4, 'five'], [True]]) d: len2([3, 4, 'five']) e: len2([4, 'five']) f: len2(['five']) g: len2([]) g: 0 f: 1 e: 2 d: 3 d: len2([[true]]) e: len2([true]) f: len2([]) f: 0 e: 1 e: len2([]) e: 0 d: 1 c: 4 b: 5 a: 6 def len2(seq): if not seq: return 0 elif isinstance(seq[0], list): return len2(seq[0]) + len2(seq[1:]) else: return 1 + len2(seq[1:])
Fallanalys Betrakta mängden av alla möjliga indata till funktionen och gruppera efter lämplig åtgärd. En tom lista ger 0 som resultat. [] [1, 2, 3] [ a, [ b ]] 47 3.14 hejsan [[ q, w ], e ] Data som inte är listor ignorerar vi. Listor som börjar med en enkel datatyp har längden 1 + längden av resten. [[13, 19, 17], [4, 5]] Listor som börjar med en underlista har längden av underlistan + längden av resten.
Översikt Repetition: Satser och uttryck Variabler, datatyper och synlighet Dynamisk typing Enkla och sammansatta typer Skuggning Upprepning, iteration och recursion Iteration handlar om att hela tiden ha ett delresultat och uppdatera det varje gång man gör upprepningssteget. Rekursion handlar om att försöka omvandla det existerande problemet till ett lite mindre problem och starta om, till dess att problemet är så litet att det är trivialt.
Glöm aldrig!
Imperativ och Funktionell Programmering i Python #TDDD73 Fredrik Heintz, IDA fredrik.heintz@liu.se @FredrikHeintz