Introduktion till programmering D0009E Föreläsning 6: Iteration Multipel tilldelning Helt ok att tilldela en variabel flera gånger: bruce = bruce, bruce = 7 bruce Output: 7 Som tillståndsdiagram: bruce 7 Kom ihåg Tilldelningen = är ett kommando som tvingar fram likhet, inte en matematisk utsaga, ej heller ett test Pga denna "destruktiva" egenskap hos tilldelningssatsen kan likheter som tidigare gällde plötsligt sluta gälla: a = # a är lika med efter denna sats b = a # b är lika med a efter denna sats a = # b är inte lika med a efter denna sats Varför vill man kunna skriva sådant krångel i Python? Svar : det vill man oftast inte! Använd därför multipel tilldelning väldigt sparsamt Svar : man kan vilja repetera samma sats flera gånger! while-satsen Ny sats, som upprepar (itererar) sin kropp så länge huvudets uttryck är sant while uttryck: satslista För att denna sats ska bli meningsfull måste uttryck kunna anta olika värden olika varv, och satslista måste kunna påverka detta Det vill säga: uttryck måste innehålla (minst) en variabel som kan ändras av satslista efter hand multipel tilldelning! 4 En gammal bekant: def (n): while n>0: n n = n- "Blastoff!" Exempel med while-sats Observera hur variabeln (parametern) n både testas och tilldelas på nytt varje varv i snurran Notera också hur n förekommer i tilldelningens högerled. Korrekt tolkning: Låt det nya värdet av n bli det gamla värdet minus ett Med while-snurra: def (n): while n>0: n n = n- "Blastoff!" 6 Snurror kontra rekursion Vilken stil är "bäst"? Med rekursion: def (n): if n>0: n (n-) else: "Blastoff!"
Snurror kontra rekursion Observation : En snurra/loop kan direkt översättas till rekursion, men inte alltid tvärt om: Rekursion är alltså kraftfullare än snurror i denna mening Observation : Enkla problem kan ofta uttryckas väldigt enkelt med snurror Observation : Svårare problem tenderar att uttryckas felaktigt med hjälp av snurror Observation 4: Snurror återanvänder minne varje varv, medan rekursion medför en ständigt växande stack Snurra: Stackdiagram för () n 0 Rekursion: -toplevel- -toplevel n n n n 0 7 8 9 Terminering Hur vet man om while-snurrans test någonsin blir falskt, så att snurran stannar? Svar: det vet man inte generellt! Programmeraren måste övertyga sig om detta från fall till fall Oändlig snurra (provkör gärna): while True: pass Jämför med rekursion som aldrig når (eller saknar) sitt basfall Hur vet vi egentligen att (n) terminerar (båda versionerna)? Hur är det med (-)? 0 Ett svårt fall Terminerar denna funktion för alla positiva värden på n? def sequence(n): while n!= : n, if n% == 0: n = n/ else: n = n*+ Svar: både bevis och motbevis saknas! (Vanligen förekommande snurror är dock mycket enklare att resonera om!) Utskrift av logaritmtabell: x =.0 while x < 0.0: x, '\t', math.log(x, ) x = x +.0 Resultat:.0 0.0.0.0.0.8496007 4.0.0.0.9809489 6.0.8496007 7.0.8074906 8.0.0 9.0.6990044 Exempel Strängen '\t' är en s k escape-sekvens som (i detta fall) betyder tabulator-tecknet Andra vanliga tecken: \n nyrad \r vagnretur \v vertikal tab \\ \ (på riktigt!) \xnn ASCII nn Exempel En variant som bara stegar igenom -potenser: x =.0 while x < 00.0: x, '\t', math.log(x, ) x = x *.0 Resultat:.0 0.0.0.0 4.0.0 8.0.0 6.0 4.0.0.0 64.0 6.0
Ett större exempel Låt oss ge oss i kast med ett program som skriver ut multiplikationstabeller! Vi kommer att bygga programmet stegvis, och på vägen illustrera två viktiga begrepp inom programutveckling: Generalisering att ersätta specifika värden med variabler och på så vis göra koden mer allmängiltig Inkapsling - att göra om fritt flytande variabler till funktionsparametrar och på så vis göra koden mer oberoende av övrig kod 4 Ett första steg Skriv ut några multipler av (en bit ur "tvåans tabell") *i, '\t', Genererar följande output: 4 6 8 0 Finns det några konstanta värden vi kan generalisera? Skulle vi kunna kapsla in denna kod, så att vi t ex inte råkar skriva över en redan befintlig variabel i? Ett andra steg Låt oss generalisera konstanten och införa variabeln n Låt oss kaspla in koden som en funktion Row Eftersom vi antar att n redan har ett värde (t ex ) så får n bli en parameter: def Row(n): n*i, '\t', 6 Ett tredje steg Vi kan nu anropa Row med olika argument och få utskrifter i stil med 6 9 8 eller 0 0 0 Låt oss göra en snurra som gör just detta! Row(i) Output: 4 6 4 6 8 0 6 9 8 4 8 6 0 4 0 0 0 6 8 4 0 6 7 Ett fjärde steg Låt oss inkapsla även denna kod som en funktion: def Table(): Row(i) (OBS mönstret: skriv kod på toppnivån testa kapsla in!) Men, frågar sig den observante, kommer det inte att bli kaos när båda funktionerna mixtrar med variabeln i? Svar: nej! Funktionerna definierar var sin variabel i 8 Just när utskrift av 6 sker i treans tabell: Table Row Stackdiagram n i i
9 Ett femte steg Låt oss generalisera Table så att den skriver ut high rader i stället för 6 def Table(high): Row(i) Vi låter high bli en parameter, eftersom koden vi generaliserar redan är en inkapslad funktion Nu kan vi skriva ut så många rader vi vill, men...... antalet kolumner är fortfarande 6! 0 Ett sjätte steg Vi generaliserar även Row genom att byta ut den hårdkodade 6:an mot ännu en parameter high def Row(n, high): n*i, '\t', def Table(high): Row(i, high) OBS: inte samma variabel, trots att de kommer att ha samma värde i detta program Resultat Effekt av Table(7): 4 6 7 4 6 8 0 4 6 9 8 4 8 6 0 4 8 0 0 0 6 8 4 0 6 4 7 4 8 4 49 Notera symmetrin längs diagonalen ett resultat av det faktum att x*y är lika med y*x Kan vi på ett enkelt sätt undertrycka de duplicerade utskrifterna (t ex de ovanför diagonalen)? Vårt sista steg Svar ja, tack vare vår tidigare parametrisering! Vi behöver bara byta raden Row(i, high) mot Row(i, i) Ny utskrift: 4 6 9 4 8 6 0 0 6 8 4 0 6 7 4 8 4 49 def Row(n, high): n*i, '\t', def Table(high): Row(i, i) Slutlig kod Poänger med funktioner Ger möjlighet att sätta illustrativa namn på satslistor Undviker duplicering av kod Möjliggör modulariserad utveckling och testning Kapslar in variabler genom att göra dem lokala Förtydligar vilka variabler som är indata genom att göra dem till parametrar Ger möjlighet till rekursion Möjliggör framtida återanvändning av uttestad kod 4 4
Hemuppgift Skriv en funktion som skriver ut alla primtal från 0 t.o.m. n på skärmen, för något n (parameter)