Uke 5 — Løkker / while / for ============================ Vi skal se på noen eksempler på while-loops og for-loops, hvor vi kan repetere instruksjoner flere ganger. I https://automatetheboringstuff.com/2e/chapter2/ finnes en god oversikt over alle tre muligheter for kontroll av programflyt vi skal bruke:: if while for .. note:: Når vi bruker løkker er det mulig å lage program som aldri avsluttes. Om du tror at koden din er i en uendelig loop kan du avslutte kjøringen ved å taste ``ctrl-c`` i terminalen som kjører koden. Eksempler --------- Eksempel 1 .......... Her er noen eksempler på while-loops. Last ned filen her: :download:`eksempel_1.py`. Prøv først å si hva som kommer til å skje når du kjører koden. Et tips er å selv gå igenom de første iterasjonene av hver loop ved hjelp av papir og penn og se hva som skjer. Kjør siden koden og se om det var riktig. .. literalinclude:: eksempel_1.py I koden ser du at det er merket med 'start', 'test' og 'update'. En while-loop er avhengig av et vilkår og kjøres om vilkåret er oppfylt. Dette er hva som er merket med 'test'. Vilkåret i while-loopen trenger mange ganger en begynnelseverdi som oppdateres i løpet av kjøringen av loopen. Dette er hva som er merket med 'start' (begynnelseverdien) og 'update' (oppdateringen) i koden. Litt senere skal vi se hva som skjer om man for eksempel ikke oppdaterer begynnelseverdien i løpet av loopen. Eksempel 2 .......... Her er noen eksempler på ``break`` og ``continue``. Man bruker ``break`` til å avbryte en løkke. Man bruker ``continue`` til å fortsette neste iterasjon av løkken før man kommer til slutten av en blokk. Last ned filen her: :download:`eksempel_2.py`. Prøv først å si hva som kommer til å skje når du kjører koden. Kjør så koden og se om det var riktig. .. literalinclude:: eksempel_2.py Se på den siste loopen. Den kan skrives om ved hjelp av if-else sånn at man ikke må bruke ``continue``. Finner du ut hvordan man kan gjøre det? Kan noen av de andre loopene skrives om sånn at vi får det samme resultatet uten å bruke ``continue``? Kan loopen med ``break`` skrives om sånn at vi får det samme resultatet uten å bruke ``break``? Både ``break`` og ``continue`` kan også brukes med for-loops som kommer nedenfor. Eksempel 3 .......... Her er noen eksempler på for-loops. Last ned filen her: :download:`eksempel_3.py`. Prøv først å si hva som kommer til å skje når du kjører koden. Kjør så koden og se om det var riktig. Merk spesielt hvilke tall du får fra loopene med ``range()``. Hvilket er det minste tallet du får? Hvilket er det største? Hva om du bruker tre tall som argument til ``range()``? Prøv selv med noen ulike tall. .. literalinclude:: eksempel_3.py Sammenligne output fra de siste to loopene. Eksempel 4 .......... Alle for-loops kan skrives om til while-loops. Her er to eksempler. Last ned og kjør filen her: :download:`eksempel_4.py`. Skjønner du hva som skjer i alle loopene? .. literalinclude:: eksempel_4.py Kan du skrive om en av for-loopene fra eksempel 3 til en while-loop? Det finnes ofte flere ulike måter til å gjøre det samme ting med programmering. Noen er enklere å bruke enn andre, og det kommer an på konteksten hva som er den beste muligheten. Når vi vet på forhånd hvor mange iterasjoner vi trenger er det vanlig å bruke ``for``. Der unngår vi at vi må håndtere startverdien, test og oppdatering av loop-verdien selv. ``while``-løkker bruker vi når vi *ikke* vet hvor mange iterasjoner vi trenger for å finne et resultat. Det kan være at stop-betingelsen er komplisert, eller at vi må ta input fra brukeren flere ganger frem til den passer. Eksempel 5 .......... Her er et eksempel på problemløsning ved hjelp av en for-loop i Python. Vi skal løse `Problem 1 `_ fra `projecteuler.net `_: If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. Find the sum of all the multiples of 3 or 5 below 1000. Så hvordan går vi fra problem til kode? Denne prosessen kan deles opp i to deler. 1. Finne ut en algoritme som løser problemet. 2. Implementere algoritmen i valgt programmeringsspråk (i vårt tilfelle, Python). Algoritme ,,,,,,,,, La oss begynne med den første delen. Vi vil finne ut et sett med instrukser som gir summen vi trenger. Om vi ville beregnet summen opp til 10 selve, med papir og penn, ville vi sannsynligvis tenkt/gjort noe slikt: * 1 er ikke et multiplum av 3 eller 5, så gjør vi ingen med dette tallet. * 2 er ikke et multiplum av 3 eller 5, så gjør vi ingen med dette tallet. * 3 er et multiplum av 3, så vi må ta med tallet i summen. Summen vår blir da 3. * 4 er ikke et multiplum av 3 eller 5, så gjør vi ingen med dette tallet. * 5 er et multiplum av 5, så vi må ta med tallet i summen. Summen vår blir da 3 + 5 = 8. * 6 er et multiplum av 3, så vi må ta med tallet i summen. Summen vår blir da 8 + 6 = 14. * 7 er ikke et multiplum av 3 eller 5, så gjør vi ingen med dette tallet. * 8 er ikke et multiplum av 3 eller 5, så gjør vi ingen med dette tallet. * 9 er et multiplum av 5, så vi må ta med tallet i summen. Summen vår blir da 14 + 9 = 23. Vi prøver å sammenfatte hva vi gjør her. Vi går igjennom tallene fra 1 til 9 og sjekker om tallet er et multiplum av 3 eller 5. Ved sånt tilfelle adderer vi tallet til summen. La oss skrive dette litt klarere, og for et godtykkelig tall :math:`N` som øvre grense: * Gå igjennom heltallen fra :math:`1` til :math:`N-1` (for eksempel 9 om den øvre grensen er 10). * For hvert tall, sjekke om tallet er et multiplum av 3 eller 5. * Om det er det, addere tallet til summen. Dette er algoritmen vår. Den er ikke avhengig av noe programmeringsspråk. Dette er bare et sett med instrukser som, om man utfører dem, gir summen fra problemet. Implementasjon ,,,,,,,,,,,,,, Nå er det dags for del 2: å implementere algoritmen i Python. Her må man finne de delene av programmeringsspråket man skal bruke. I dette tilfelle vil vi gjøre noe for alle tall fra 1 til den øvre grensen (som vi gir navnet ``max_N``). Dette kan vi for eksempel gjøre med en ``for``-loop over ``range(max_N)``. Denne går fra og med null til og med ``max_N - 1``:: max_N = 10 for n in range(max_N): Vi må også definere summen før loopen (den er null fra begynnelsen), ellers har vi ingen summe å addere det første tallet til. Så vi lager en variabel ``summe`` som har verdien null, før for-loopen:: max_N = 10 summe = 0 for n in range(max_N): For hver iterasjon av for-loopen skal vi sjekke om det aktuelle tallet ``n`` er et multiplum av 3 eller 5. Dette kan vi gjøre med et if-statement innen for-loopen. Her sjekker vi om tallet er deleligt med 3 eller 5 (det er samme som at det er et multiplum av 3 eller 5) med koden ``n % 3 == 0 or n % 5 == 0``. Om det er det adderer vi tallet til summen med koden ``summe += n``. Ellers skal vi ikke gjøre noe, så det er ingen mer kode innen for-loopen. Her er koden. Du kan laste ned filen her: :download:`eksempel_5.py`. Kjør koden og endre på ``max_N`` sånn at du kan svare på problemet. Hva er summen av alle multiplumer av 3 og 5 som er mindre enn 1000? .. literalinclude:: eksempel_5.py Eksempel 6 .......... Her er noen eksempler på kode som ikke er riktig. Når vi bruker while-loops er det mulig å lage program som aldri avsluttes om vilkåret i while-loopen alltid er oppfylt. Om du tror at koden din er i en uendelig loop kan du avslutte kjøringen ved å for eksempel taste Ctrl+C i terminalen som kjører koden. Last ned filen her: :download:`errors_1.py`. Prøv først om du kan finne feilen i koden. Kjør siden koden og se hva som skjer. Endre koden slik at den går å kjøre, og ikke blir fast i en uendelig loop. .. literalinclude:: errors_1.py Spørsmål -------- Se også `"Practice Questions" 1. - 13. `_ 1. Lag en ``while``-løkke som skriver ut alle partall fra 5 til 25 2. Lag en løkke med ``while True`` og ``break`` som bruker ``input()`` frem til brukeren skriver ``q``. 3. Lag en ``for``-løkke som skriver ut 3, 6, 9, 12, 15. Obligatoriske oppgavene ----------------------- **Oppgavene skal leveres på git.app.uib.no innenfor 2020-09-25 23:59** Del 1: DNA-komplementstreng ........................... En DNA-sekvens (f.eks ``ATAGCAGT``) sammensettes av 4 ulike "baser", som beskrives med ``A``, ``T``, ``G`` og ``C``. Under replikasjon av sekvensen kan hver base settes sammen med eksakt én av de andre. ``A`` med ``T``, og ``C`` med ``G``:: original -------> ATAGCAGT |||||||| TATCGTCA <------- complement Slik får man en "komplementstreng" av den opprinnelige DNA-sekvensen. I en fortløpende tekst er det vanlig å skrive ned komplementstrenger **baklengs**, slik at i vårt eksempel, vi sier at ``ATAGCAGT`` og ``ACTGCTAT`` er komplementstrenger av hverandre. **Lag en funksion** ``complement`` som returnerer komplementstrengen av en DNA-sekvens. Funksjonen skal ta inn én streng som argument, og skal returnere én streng. Du kan anta at alle input-strenger er gyldige DNA-sekvenser. Del 2: Primfaktorer ................... **Lag en funksjon** ``print_prime_factors`` som tar et heltall som argument og skriver ut alle primfaktorer av tallet fra minst til størst. En enkel måte å gjøre det er å dividere ut alle ``2``-faktorer som finnes, så alle ``3``-faktorer, osv. Eksempelkjøring:: Input: 10 2 5 Input: 27 3 3 3 Input: 28 2 2 7 Input: 53 53 Del 3: ASCII-art ................ **Lag en funksjon** ``print_diamond`` som tar et heltall ``N`` som argument og skriver ut et diamantmønster med sidelengde ``N`` slik som i eksemplene:: Tall: 1 X Tall: 2 X XXX X Tall: 3 X XXX XXXXX XXX X Tall: 4 X XXX XXXXX XXXXXXX XXXXX XXX X Del 4: `Project Euler nr.9 `_ ................................................................. Et pytagoreisk trippel består av tre *positive* heltall :math:`a < b < c`, slik at :math:`a^2 + b^2 = c^2` (f.eks :math:`3^2 + 4^2 = 9 + 16 = 25 = 5^2`). Det finnes kun et pytagoreisk trippel slik at :math:`a + b + c = 1000`. Finn produktet :math:`abc`. **Skriv en funksjon** ``pytag_trippel(sum_abc)`` som tar et argument ``sum_abc``. Loope over tallene ``a, b, c`` slik at ``a+b+c == sum_abc``. Når du finner et trippel ``a b c`` som tilsvarer beskrivelsen ovenfor, skal funksjonen returnere ``a*b*c``. For å svare *Project Euler* tester vi med ``pytag_trippel(1000)``. Tips: etter du velger :math:`a` og :math:`b`, må du sjekke bare én verdi for :math:`c`.