Funktioner TDDD64 Programmering i Python Föreläsning 7 Peter Dalenius Institutionen för datavetenskap 2013-10-02
Prova på-laborationer Förutsättningar 3 st x 4 timmar under de närmaste veckorna Genomförs i par, till exempel men inte nödvändigtvis de par ni eventullt har bildat inför laboration 3 och framåt Redovisas muntligen på plats Kräver inget för- eller efterarbete Eventuell håltid används för att göra klart datorintroduktionsmaterialet och tillhörande slutuppgift (deadline torsdag 24/10) Syfte och innehåll Bredda synen på programmering och programspråk Enkla övningar i Haskell, Prolog och SQL 2
Översikt Teori (men med många exempel) Varför använder vi funktioner? Vad händer när man anropar en funktion? När är variabler och funktioner synliga? Praktik (men teoretiskt förankrad) Hur lär man sig programmera? Hur testar man sina program? 3
Funktioner # Specifik, hårdkodad, version def beer_song(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") # Parametriserad, mer generell version def beverage_song(n, bev): for i in range(n,0,-1): print(i, "bottles of", bev, "on the wall,", i, "bottles of", bev) print("take one down, pass it around,", i-1, "bottles of", bev) 4
Funktioner def farm_verse(animal, noise): print("old MacDonald had a farm, EE-I-EE-I-O") print(" And on that farm he had a", animal, "EE-I-EE-I-O") print(" With a", noise, noise, "here and a", noise, noise, "there") print(" Here a", noise, "there a", noise, "everywhere a", noise, \ noise) print(" Old MacDonald had a farm, EE-I-EE-I-O") def farm_song(): farm_verse("cow", "moo") farm_verse("pig", "oink") farm_verse("cat", "meow") farm_verse("duck", "quack") 5
Funktioner def farm_verse(animal, noise): print("old MacDonald had a farm, EE-I-EE-I-O") print(" And on that farm he had a", animal, "EE-I-EE-I-O") print(" With a", noise, noise, "here and a", noise, noise, "there") print(" Here a", noise, "there a", noise, "everywhere a", noise, \ noise) print(" Old MacDonald had a farm, EE-I-EE-I-O") def farm_song(): for pair in (("cow", "moo"), ("pig", "oink"), ("cat", "meow"), \ ("duck", "quack")): farm_verse(pair[0], pair[1]) 6
Poängen med funktioner Varför använder vi funktioner, istället för att ha all programkod i en stor hög? Därför att vi undviker upprepning av kod, vilket gör den... kortare och enklare lättare att testa och underhålla Därför att vi skapar ordning i koden och därmed uppnår... läsbarhet, genom beskrivande namn på funktioner och variabler (procedurabstraktion) överblickbarhet, genom att gruppera funktioner med likartade uppgifter (modularisering) 7
Vad händer vid ett funktionsanrop? 1. De aktuella parametrarna (argumenten) beräknas. 2. De formella parametrarna tilldelas dessa värden. def farm_song(): farm_verse("cow", "moo") farm_verse("pig", "oink") farm_verse("cat", "meow") farm_verse("duck", "quack") def farm_verse(animal, noise): print("old MacDonald...") animal = "cow" noise = "moo" 4. Programkontrollen återförs till en punkt efter anropet. 3. Funktionens kropp exekveras (körs). 8
Parameteröverföring: Exempel 1 def lägg_till_ränta(saldo, ränta): nytt_saldo = saldo * (1 + ränta) saldo = nytt_saldo def test(): saldo = 1000 ränta = 0.05 lägg_till_ränta(saldo, ränta) print(saldo) >>> test() 1000 9
Parameteröverföring: Exempel 1 def test(): saldo = 1000 ränta = 0.05 lägg_till_ränta(saldo, ränta) print(saldo) saldo = 1000 ränta = 0.05 def lägg_till_ränta(saldo, ränta): nytt_saldo = saldo * (1 + ränta) saldo = nytt_saldo saldo = 1000 1050.0 ränta = 0.05 nytt_saldo = 1050.0 10
Parameteröverföring: Exempel 2 def lägg_till_ränta(saldo, ränta): nytt_saldo = saldo * (1 + ränta) return nytt_saldo def test(): saldo = 1000 ränta = 0.05 saldo = lägg_till_ränta(saldo, ränta) print(saldo) >>> test() 1050.0 11
Parameteröverföring: Exempel 2 def test(): saldo = 1000 ränta = 0.05 saldo = lägg_till_ränta(saldo, ränta) print(saldo) saldo = 1000 1050.0 ränta = 0.05 def lägg_till_ränta(saldo, ränta): nytt_saldo = saldo * (1 + ränta) return nytt_saldo saldo = 1000 ränta = 0.05 nytt_saldo = 1050.0 12
Parameteröverföring: Exempel 3 def lägg_till_ränta(saldon, ränta): for i in range(len(saldon)): saldon[i] = saldon[i] * (1 + ränta) def test(): saldon = [1000, 2500, 400] ränta = 0.05 lägg_till_ränta(saldon, ränta) print(saldon) >>> test() [1050.0, 2625.0, 420.0] 13
Parameteröverföring: Exempel 3 def test(): saldon = [1000, 2500, 400] ränta = 0.05 lägg_till_ränta(saldon, ränta) print(saldon) saldon = ränta = 0.05 def lägg_till_ränta(saldon, ränta): for i in range(len(saldon)): saldon[i] = saldon[i] * (1 + ränta) saldon = = 1000 ränta = 0.05 1050.0 1000 2625.0 2500 420.0 400 14
Parameteröverföring Vad menar vi med värde egentligen? Värdet av en enkel datatyp är innehållet i sig, d.v.s. själva talet eller motsvarande. Värdet av en sammansatt datatyp är en referens till en plats i minnet där innehållet är lagrat. (Det är detta som gör att en lista kan utökas eller minskas, och att en lista som ingår i en tupel kan ändras, även om tupeln i sig inte kan ändras.) Det som överförs från den anropande till den anropade funktionen är just argumentenas värden. Detta innebär att enkla datatyper (t.ex. tal) överförs som kopior och att källan till värdet inte kan ändras av den anropade funktionen att sammansatta datatyper överförs som referenser och att källan till värdet kan ändras, om datatyper är muterbar (som t.ex. listor) 15
Parameteröverföringsmodeller Call-by-reference brukar innebära att det som kommer in till funktionen är referenser till data, och att funktionen mycket väl kan ändra detta. Det stämmer ju inte för enkla datatyper i Python, och som vi såg kommer tilldelningar till den formella parametern inte att synas utanför funktionen. Call-by-value brukar innebära att man beräknar det som skickas in till funktionen och skickar värdet. Det stämmer rätt bra, men som vi ser kan man ändra inuti listor m.fl. muterbara datastrukturer, så det fångar inte riktigt hela sanningen. Call-by-sharing är det mest exakta sättet att beskriva hur Python funkar, men det är inte ett särskilt väletablerat begrepp. 16
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. 17
Exempel på olika nivåer av synlighet a = 1 Synlighet för symbolerna a, yttre def yttre(b): c = 3 def inre(d): e = 5 return a+b+c+d+e return inre(4) Synlighet för symbolerna b, c, inre Synlighet för symbolerna d, e print(yttre(2)) 18
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. 19
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 20
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. 21
Sammanfattning parametrisering att göra en funktion mer generell genom att låta en del av innehållet styras av en (ny) parameter istället för att vara hårdkodat procedurabstraktion att sätta ett namn på en följd av detaljerade instruktioner och därefter inte bry sig så mycket om dessa, utan enbart tala om instruktionerna i termer av det nya namnet parameteröverföring hur information kommer till/från en funktion synlighet/räckvidd/scope den del av programmet där en viss symbol (variabel eller funktion) går att använda LEGB minnesregel för de fyra principiella nivåerna av synlighet i Python skuggning att två symboler på olika nivåer kan ha samma namn, men att enbart den inre är giltig, dock utan att förstöra den yttre 22
23 Hur ska man göra för att lära sig?
Vad är lärande? A process which takes place within the organism and is inferred from specified changes taking place in the organism's behavior J. F. Hall (1966) The Psychology of Learning Acquiring new, or modifying existing, knowledge, behaviors, skills, values, or preferences Wikipedia (2012) Att få förmågan att erfara världen på särskilda sätt F. Marton & S. Booth (1997) Om lärande 24
Vad händer när man lär sig? kunskap kunskap tid tid 25
Vilka erfarenheter av lärande har du? Vad har du lärt dig? Hur gick det till? Finns det något som du kan lite bättre än folk i allmänhet? Tennis Köra bil Schack Tala franska 26
Hur påverkar detta dig? Vad kan du göra? Osäkerhet är en naturlig del av lärandet som man måste lära sig hantera. Bygg upp ett realistisk självförtroende genom att bli bättre på att bedöma dig själv. Alla hantverk, som programmering, kräver mängdträning. 27
Testning: Fyra nivåer Enhetstest (eng. unit testing) Varje enhet, t.ex. funktion, testas för sig. Integrationstest Test av flera enheter tillsammans. Systemtest Hela systemet testas, t.ex. säkerhet, prestanda, användbarhet. Acceptanstest Slutligt test av kunden. 28
Testning: Två strategier Black box test Den som utformar testerna har inte sett koden utan utgår från specifikationen. White box test Testerna baseras på analys av programkoden. 29
Enhetstestning Hur gör man? Grundtanken är att vi formulerar ett antal testfall förväntningar på utdata givet vissa indata och paketerar ihop dessa i en testsvit som kan köras automatiskt. Om man ska göra detta systematiskt använder man gärna något testramverk, men dessa är lite för stora för de funktioner vi än så länge har skrivit i kursen. Därför kommer vi att skriva testkod själva. Vad hoppas vi uppnå? Underlätta förändring Underlätta integrationstestning I viss utsträckning ersätta design och dokumentation 30
Metod 1: Assertions def remove(seq, x): if not seq: return [] elif isinstance(seq[0], list): return [remove(seq[0], x)] + remove(seq[1:], x) elif seq[0] == x: return remove(seq[1:], x) else: return [seq[0]] + remove(seq[1:], x) assert remove([], 7) == [] assert remove([1, 2, 3], 2) == [1, 3] assert remove([1, 2, [3, 2, 4], 5], 2) == [1, [3, 4], 5] Körs automatisk när modulen/filen laddas in. 31
Metod 2: Särskild testfunktion def test_sort(): testcases = (([], []), \ ([3, 2, 1], [1, 2, 3]), \ (['c', 'a', 'b'], ['a', 'b', 'c']), \ (['a', 'b'], ['a', 'b'])) for case in testcases: case[0].sort() assert case[0] == case[1] test_sort() Bra att samla testfall i en lista eller tupel för att lättare kunna utöka vid behov och testfunktionen kan köras när som helst. 32
Vilka testfall behövs? Utforma testfall så att alla delar av funktionen körs någon gång, t.ex. alla alternativ i en sammansatt if-sats. Testa extremvärden, d.v.s. 0, 1, tomma strängen, tomma listan. Testa normalfallet. (Vad är det mest typiska invärdet?) Testa olika typer av indata (om det är tänkt att funktionen ska kunna hantera det). Använd inte flera testfall som testar precis samma sak. Nästa lektion (fredag) kommer handla om testning och felsökning! 33
Bildkällor: http://en.wikipedia.org/wiki/file:france_in_xxi_century._school.jpg http://commons.wikimedia.org/wiki/file:all_gizah_pyramids.jpg http://commons.wikimedia.org/wiki/file:amazona_pretrei_-rio_grande_do_sul_-brazil-8e-2c.jpg http://commons.wikimedia.org/wiki/file:dashboard_volvo_v60.jpg?uselang=sv http://commons.wikimedia.org/wiki/file:eiffel_trocadero_i.jpg http://commons.wikimedia.org/wiki/file:nadal_australian_open_2009_5.jpg http://commons.wikimedia.org/wiki/file:checkmate.jpg http://commons.wikimedia.org/wiki/file:frustrated_man_at_a_desk.jpg http://commons.wikimedia.org/wiki/file:rafa_celebrates.jpg http://commons.wikimedia.org/wiki/file:wood_carving,_laos.jpg www.liu.se