Kapittel 5 Løkker


Tipp topp at du jobbet mye med lister i forrige kapittel. I dette kapittelet skal du gjøre et dypdykk inn i konseptet løkker. Du vil bruke løkker til å gjenta lignende handlinger flere ganger. I løpet av kapittelet skal du lære både for-løkker og while-løkker, som er de to hovedtypene med løkker i Python. I tillegg skal du lære om nøkkelordene break og continue som hjelper deg med å bryte ut av løkker. Etter dette kapittelet kan du lage programmer som håndterer langt mer kompliserte situasjoner.

5.1 Introduksjon - Kode som gjentar lignende handlinger

En situasjon som er veldig vanlig i programmering er at du må gjenta en lignende handling flere ganger. La oss gi et par eksempler på hvordan dette kan føre til problemer for en begynnende programmerer.

5.1.1 Eksempel 1: Nyansatt i Statistisk sentralbyrå

Etter å ha blitt ansatt i Statistisk sentralbyrå for din skinnende personlighet har du raskt blitt satt til å regne ut gjennomsnittlig levealder til kvinner i byen Førde. Litt rart, men en jobb er en jobb. Du har fått en liste med alle aldrene til registrerte kvinner i Førde. Du bryter ned problemet til å først se på kun de fem første kvinnene på listen. Her er koden du kom fram med for å regne ut gjennomsnittet:

aldre = [42, 21, 33, 65, 17]
sum_aldre = 0
sum_aldre += aldre[0]
sum_aldre += aldre[1]
sum_aldre += aldre[2]
sum_aldre += aldre[3]
sum_aldre += aldre[4]
gjennomsnitt_aldre = sum_aldre / len(aldre)
print(f'Gjennomsnitt: {gjennomsnitt_aldre}')

Her tar du kvinnenes aldre og legger dem til verdien sum_aldre en etter en. Til slutt regner du ut gjennomsnittsalderen. Du skal ha gratulasjon for god bruk av lister. Dette er likevel ganske tungvint! Hvor mange linjer med kode trenger du for å skrive dette ferdig? Et raskt Wikipedia-søk viser det at det bor over 10.000 mennesker i Førde. Det blir visst en lang helg.

5.1.2 Eksempel 2: Nyansatt i Cappelen Damm

Jobben hos Statistisk sentralbyrå gikk ikke så bra, men en ny dag gir nye muligheter. Du er nå nyansatt i det store norske forlaget Cappelen Damm. Med litt kunnskap om programmering blir du raskt satt til å søke gjennom norske bøker for å finne ut hvor de inneholder de særnorske bokstavene ‘æ’, ‘ø’, og ‘å’. Du blir fortalt at dette er viktig for digitale utgaver, og nøyer deg med det. En hel boksamling høres litt ambisiøst ut, så du begynner heller med et enkelt norsk ord som 'Hallå'. Koden du kom fram til etter litt prøving og feiling ser slik ut:

norsk_ord = 'Hallå'

særnorske_bokstaver = ['æ', 'Æ', 'ø', 'Ø', 'å', 'Å']

if norsk_ord[0] in særnorske_bokstaver:
    print('Vi fant en særnorsk bokstav i posisjon 0.')
  
if norsk_ord[1] in særnorske_bokstaver:
    print('Vi fant en særnorsk bokstav i posisjon 1.')
  
if norsk_ord[2] in særnorske_bokstaver:
    print('Vi fant en særnorsk bokstav i posisjon 2.')
  
if norsk_ord[3] in særnorske_bokstaver:
    print('Vi fant en særnorsk bokstav i posisjon 3.')
  
if norsk_ord[4] in særnorske_bokstaver:
    print('Vi fant en særnorsk bokstav i posisjon 4.')

Hm…Svetten begynner å renne nedover pannen din. Du innser at dette er samme felle som hos Statistisk sentralbyrå. Her har du en handling, nemlig å søke gjennom hver bokstav i et ord, som er svært repeterende. Skal du søke gjennom en boksamling på denne måten kommer du ikke til å kunne ta ut mange feriedager. Uff da.

5.1.3 Hva er redningen?

Frykt ikke! Programmeringsspråk har en redning på begge problemene ovenfor kalt løkker48. Det er løkker som gjør at du kan med enkel kode gjøre en handling mange ganger. Hos Statistisk sentralbyrå i Eksempel 1 ovenfor kunne du brukt koden:

aldre = [42, 21, 33, 65, 17]
sum_aldre = 0
for alder in aldre:
    sum_aldre += alder
gjennomsnitt_aldre = sum_aldre / len(aldre)
print(f'Gjennomsnitt: {gjennomsnitt_aldre}')

Du trenger ikke forstå koden over ennå. Det sentrale er at koden her gjør nøyaktig det samme som tidligere, bare at den ikke er avhengig av lengden til listen aldre. Utvider du listen aldre til å inkludere aldrene til alle kvinner i Førde så er det ingen annen del av koden som må endres.

Løkker ville også reddet dagen hos Cappelen Damm i Eksempel 2. Der kunne du nemlig skrevet koden slik:

norsk_ord = 'Hallå'

særnorske_bokstaver = ['æ', 'Æ', 'ø', 'Ø', 'å', 'Å']

for i in range(len(norsk_ord)):
    if norsk_ord[i] in særnorske_bokstaver:
        print(f'Vi fant en særnorsk bokstav i posisjon {i}.')

Denne koden fungerer uansett hvor lang teksten er. Her er en hel setning:

norsk_setning = 'Hallå folkens, har dærre lyst på en kopp kaffi!'

særnorske_bokstaver = ['æ', 'Æ', 'ø', 'Ø', 'å', 'Å']

for i in range(len(norsk_setning)):
    if norsk_setning[i] in særnorske_bokstaver:
        print(f'Vi fant en særnorsk bokstav i posisjon {i}.')

Dette kapittelet skal lære deg hvordan du skriver løkker for å løse forskjellige problemer. Aldri igjen må du flykte fra en fiktiv jobb. Du vil nemlig bli en ekspert på løkker!

5.2 Enkle for-løkker

Det første du trenger å vite er at det finnes to nøkkelord i Python for å skrive løkker. Dette er nøkkelordene for og while. Løkker som benytter seg av nøkkelordet for kaller vi for for-løkker49, og tilsvarende for while-løkker50. Vi starter med nøkkelordet for og kommer tilbake til nøkkelordet while i seksjon 5.4.

La oss begynne med å analysere det enkleste eksempelet på en for-løkke:

for i in range(1, 8):
    print(i)

La oss bryte ned hva som skjer i koden ovenfor. Funksjonen range() er en enkel måte å lage en sekvens med heltall i stigende rekkefølge. Koden range(1, 8) gir oss alle heltall fra 1 til, men ikke inkludert, 8. Deretter setter vi variabelen i lik 1 og skriver ut verdien. Etter dette blir i satt til å være 2, som også blir skrevet ut. Dette fortsetter helt frem til i er lik 7, som blir skrevet ut på samme måte. Så er det slutt. Vi kan lese koden ovenfor muntlig for oss selv som for hvert tall fra 1 til 7 så skriver vi ut tallet.

Vi kan gjøre oss noen observasjoner om koden ovenfor:

  • Vi har valgt å bruke navnet i for variabelen som i løpet av for-løkken tar verdiene fra 1 til og med 7. Men denne kan vi kalle hva som helst. Ofte er i eller indeks brukt som et standardvalg dersom situasjonen ikke byr på et bedre navn.

  • Hvert nye valg av i i løkken kalles en iterasjon51. Når i er lik 1 er vi i den første iterasjonen av for-løkken, mens når i er lik 7 er vi i den siste iterasjonen av for-løkken.

  • Merk at etter symbolet : på slutten av første kodelinje så blir neste linje innrykket på et eget horisontalt nivå. Dette har vi sett tidligere i kapittel 3 når vi arbeidet med betingede setninger. Vi sier også her at koden som er innrykket til høyre med 4 mellomrom er en kodeblokk som tilhører for-løkken.

Den beste måten å lære seg for-løkker er å eksperimentere litt. Hva om du ønsker å skrive ut tallene fra 5 til og med 12 i stedet? Da endrer du bare verdiene i funksjonen range() selvfølgelig!

for i in range(5, 13):
    print(i)

Dette var enkelt nok. Hva om du vil skrive ut hvert tall multiplisert med seg selv? Da endrer du bare logikken i kodeblokken:

for i in range(1, 8):
    print(i * i)

Det spiller ingen rolle for Python hva du kaller variabelen i for-løkken, så lenge den heter det samme i oppsettet av for-løkken og i kodeblokken:

for hva_som_helst in range(1, 8):
    print(hva_som_helst * hva_som_helst)

Det er også nyttig å vite at dersom du skriver range(8) så er dette en forkortelse for range(0, 8). Så hvis du bare spesifiserer ett positivt heltall a, slik som i range(a), så er det automatisk tolket som range(0, a). Denne snarveien skal vi bruke mye når vi ønsker å starte range() fra verdien 0.

La oss nå se på et eksempel som gjør mer enn bare å skrive ut tall.

5.2.1 En tallnøtt

Vi skal nå bruke for-løkker til å regne ut summen av alle heltall fra 1 til og med 100. Spørsmålet er altså hva er summen

\[1 + 2 + \dots + 100 = \, ?\] Dette er ganske slitsomt å gjøre på en vanlig kalkulator. La oss tenke litt høyt sammen om hva som må til for å regne dette ut i Python:

  • Vi må ha en variabel vi kan kalle sum_av_tallene som representerer summen. Denne variabelen kan vi sette til verdien 0 i begynnelsen, og deretter legge til tallene fra 1 til og med 100.

  • Vi må bruke en for-løkke til å gå gjennom alle tallene fra 1 til og med 100 og legge dem til variabelen sum_av_tallene.

  • Vi må til slutt skrive ut sum_av_tallene slik at vi ser hva den er.

Prøv selv å skrive koden før du ser på løsningen under.

sum_av_tallene = 0
for tall in range(1, 101):
    sum_av_tallene += tall

print(f'Summen av de 100 første tallene er: {sum_av_tallene}')

Tipp topp! For noen som prøver å skrive dette for første gang er det fristende å ha variabelen sum_av_tallene inne i kodeblokken som tilhører for-løkken:

for tall in range(1, 101):
    sum_av_tallene = 0
    sum_av_tallene += tall

print(f'Summen av de 100 første tallene er: {sum_av_tallene}')

Dette gir oss et feil svar! Kjører du koden over vil du få skrevet ut at summen er lik 100. Selv om vi ikke vet hva summen av alle tallene fra 1 til og med 100 er, så er det definitivt mer enn 100. Hvis du leser koden over nøye så vil du se at variabelen sum_av_tallene blir satt tilbake til 0 i hver iterasjon av for-løkken. Da blir alle tallene før 100 glemt og vi sitter igjen med et feil svar.

Nå som skepsisen din er vekket, er vi sikker på at 5050 som vi fikk først er riktig da? En listig måte å overbevise seg selv om at dette er riktig er å pare sammen tallene i hver ende slik:

\[\begin{align} 1 + 100 & = 101 \\ 2 + 99 & = 101 \\ 3 + 98 & = 101 \\ 4 + 97 & = 101 \\ \vdots \\ 50 + 51 & = 101 \end{align}\]

Her får vi 101 hver gang. Ved å bruke dette trikset kan vi stokke om summen slik at vi får

\[\begin{align} 1 + 2 + \dots + 100 & = \\ (1 + 100) + (2 + 99) + \dots + (50 + 51) & = \\ 101 + 101 + \dots + 101 & = \\ 50 * 101 & = \\ 5050 \end{align}\]

Den kjente matematikeren Carl Friedrich Gauss brukte ifølge legenden visstnok dette trikset mens han gikk på barneskolen til å komme frem til at summen blir 5050. Selv om vårt fokusområde er programmering og ikke matematikk er det nyttig å se hvor kort vei det blir mellom dem iblant. Vi får være takknemlige for at Carl Friedrich Gauss ikke hadde Python på sin tid, slik at det fortsatt er problemer igjen for oss å løse.

5.3 Mer kompliserte for-løkker

Nå som vi kan skrive en enkel for-løkke så er det nyttig å se hvordan de passer sammen med det vi har lært tidligere. Dette vil la oss skrive mer kompliserte for-løkker som kan løse flere typer problemer.

5.3.1 For-løkker kombinert med lister

La oss først se på hvordan for-løkker kan kombineres med lister. Denne kombinasjonen kunne løst problemet vi støtte på i seksjon 5.1 hos Statistisk sentralbyrå med enkelhet.

For-løkker brukes sammen med lister til å gå gjennom hvert element i en liste og gjøre en handling. Sett at vi har en liste med personer. Hvordan kan vi skrive hvert enkelt navn til terminalen? Med en for-løkke så klart!

personer = ['Eirik', 'Stine', 'Theo']
for indeks in range(len(personer)):
    print(personer[indeks])

Les koden over nøye. Etter å ha laget listen personer så skriver vi en for-løkke der variabelen indeks går gjennom tallene 0, 1, og 2. For hver iterasjon så printer vi ut navnet som har posisjonen indeks i listen personer.

La oss lage en ny liste der alle navnene er skrevet med store bokstaver. Hvis vi begynner med listen ['Eirik', 'Stine', 'Theo'] over ønsker vi altså å sitte igjen med en ny liste på formen ['EIRIK', 'STINE', 'THEO']. En måte å gjøre dette på er å skrive koden:

personer = ['Eirik', 'Stine', 'Theo']
for indeks in range(len(personer)):
    personer[indeks] = personer[indeks].upper()
  
print(personer)

For hver person så bytter vi ut navnet med det samme navnet etter at strengmetoden .upper() er brukt. Hadde listen personer hatt 10 000 navn i seg, så ville dette vært utrolig besparende fremfor å endre listen manuelt.

I koden over endret vi den originale listen, slik at vi ikke lengre har informasjonen i den originale listen. Det er også mulig å lage en helt ny liste slik:

personer = ['Eirik', 'Stine', 'Theo']
personer_store_bokstaver = []
for indeks in range(len(personer)):
    personer_store_bokstaver.append(personer[indeks].upper())
  
print(personer_store_bokstaver)

Hva er fordelene og ulempene med å endre den originale listen vs. å lage en ny liste? Dersom du endrer listen så mister du den originale listen. I veldig mange tilfeller ønsker man å ha begge listene, så da er dette uønsket. Et unntak fra dette er når listen er så stor at å lage en ny liste er ressurskrevende. I slike tilfeller kan det være greit å endre den originale listen slik at man ikke sitter med dobbelt så mye data å holde styr på. Jeg ville som en tommelfingerregel valgt å lage en ny liste i de fleste tilfeller, med mindre du har en god grunn til å endre den originale listen.

5.3.2 For-hver-løkker

I kombinasjon med lister er det også en annen måte å skrive for-løkker på. Dette kalles noen ganger for-hver-løkker52, men det er ikke uvanlig at dette også bare blir kalt en for-løkke. For-hver-løkker er motivert av følgende observasjon: Ofte trenger vi ikke holde rede på indeksen når vi jobber med lister. Se igjen på koden vi skrev tidligere:

personer = ['Eirik', 'Stine', 'Theo']
personer_store_bokstaver = []
for indeks in range(len(personer)):
    personer_store_bokstaver.append(personer[indeks].upper())
  
print(personer_store_bokstaver)

Her dukker posisjonen indeks opp to ganger i koden. Men vi er egentlig ikke interessert i indeksen til elementene, bare i elementene i seg selv. Det spiller ingen rolle om 'Eirik' har indeks 0, 1, eller 2 for operasjonen som skal utføres. Det eneste som er viktig er at elementene i den nye listen har samme rekkefølge som den originale listen.

Motivert av dette er det også en annen måte å skrive dette på i Python:

personer = ['Eirik', 'Stine', 'Theo']
personer_store_bokstaver = []
for person in personer:
    personer_store_bokstaver.append(person.upper())
  
print(personer_store_bokstaver)

Ved å bruke en for-hver-løkke så bryr vi oss ikke om indekser, men bare om elementene. Dette gir oss mindre kode og det er lettere å skrive koden

for person in personer:

riktig fremfor koden

for indeks in range(len(personer)):.

Det er viktig at du forstår at for-hver-løkker ikke alltid kan erstatte vanlige for-løkker. Eksempelet vi gjorde tidligere i kapittelet med å summere opp alle tallene mellom 1 og 100 er best å løse med en for-løkke og range(). Skulle vi derimot summert opp alle tallene mellom 1 og 100 i en liste vi allerede hadde tilgang til, så hadde en for-hver-løkke vært et godt valg.

5.3.3 For-løkker kombinert med betingede setninger

La oss nå se på hvordan for-løkker kan kombineres med betingede setninger. Denne kombinasjonen ville latt oss løse problemet vi støtte på i introduksjonen hos forlaget Cappelen Damm.

Betingede setninger lar oss gjøre en handling med utvalgte elementer i en for-løkke. Sett at vi har en liste med personer og ønsker å skrive ut til terminalen de navnene som begynner på bokstaven 'E'. For å gjøre dette kan vi skrive følgende kode:

personer = ['Eirik', 'Stine', 'Theo']
for person in personer:
    if person[0] == 'E':
        print(person)

Her kombinerer vi for-hver-løkken vi lærte tidligere med en if-setning. På denne måten blir bare navnet 'Eirik' skrevet ut.

Hvis du ser tilbake på utfordringen hos Cappelen Damm i introduksjonen så løser dette problemet. Der var det bare noen av bokstavene, nemlig de særnorske bokstavene, som skulle skrives ut til terminalen. Vi kan også bruke en if-else-setning eller if-elif-else-setning dersom vi ønsker mer kompleks funksjonalitet.

Et vanlig scenario er at du må skrive inn telefonnummeret ditt når du registrerer deg hos en nettjeneste. Selv om de fleste greier å skrive inn telefonnummeret sitt riktig, så er det veldig lett å taste for svakt eller for hardt på tastaturet. Resultatet blir ofte at telefonnummeret du skriver inn får 7 eller 9 siffer. I Norge skal telefonnumre ha 8 siffer. Hvordan kan vi kategorisere telefonnumre som for lange eller for korte? Dette er en enkel sak med Python:

telefonnumre = ['95483256', '1437221', '768503578', '63831148']
for nummer in telefonnumre:
    if len(nummer) < 8:
        print(f'Nummeret {nummer} er for kort. Kontakt brukeren på epost!')
    elif len(nummer) > 8:
        print(f'Nummeret {nummer} er for langt. Kontakt brukeren på epost!')
    else:
        print(f'Nummeret {nummer} har riktig lengde. Ikke kontakt brukeren!')

Les koden over nøye. Det er ikke noe i koden som du ikke har sett tidligere, men den kombinerer mange viktige konsepter. Hvis nettjenesten din får 500 nye brukere hver dag så er slik kode utrolig nyttig. Når du lærer mer Python vil du til slutt være i stand til å sende oppfølgings-epost til brukerne helt automatisk ved å bruke kode. Alternativet hadde vært å manuelt sjekke lengden til 500 telefonnummer hver eneste dag. Dette høres ikke ut som en jobb for en med dine Python-kunnskaper.

5.4 While-løkker

Så langt har vi sett på for-løkker og variasjonen med for-hver-løkker. Begge typene for-løkker er nyttig når vi på forhånd vet hvor mange iterasjoner løkken vår skal ha. Men ikke alle situasjoner er slik.

Et eksempel hvor antall iterasjoner er ukjent er den populære leken stein-saks-papir. La oss si at de to spillerne gir seg når en av dem har oppnådd 3 poeng. Det vil minst ta 3 runder (iterasjoner) før spillet avsluttes, men kan også ta flere. Begge spillerne kan for eksempel begynne med papir første runde. Da er stillingen fortsatt 0-0, og vi kan nå med sikkerhet si at spillet vil ta minst 4 runder. Men hvor mange? Dette er ikke mulig å si på forhånd.

I en slik situasjon er for-løkker ikke ideelt. Når antall iterasjoner er ukjent fra starten av brukes heller while-løkker. En while-løkke er en løkke der vi ikke spesifiserer antall iterasjoner på forhånd, men heller et krav for når løkken skal avsluttes. La oss begynne med å lære hvordan vi skriver enkle while-løkker.

5.4.1 Enkle while-løkker

Her har du et eksempel på en enkel while-løkke:

i = 1
while i < 8:
    print(i)
    i += 1

Les koden over nøye. Vi må først sette variabelen i til å være lik 1 før løkken. Så bruker vi nøkkelordet while sammen med et sannhetsuttrykk I dette tilfellet i < 8. I kodeblokken skriver vi ut i og deretter øker verdien til i med 1. While-løkken gjentar seg selv helt til i < 8 ikke lenger er sant. Dette er når i = 8. Da blir ikke kodeblokken til while-løkken utført og programmet er ferdig. Vi har skrevet ut tallene fra 1 til 7.

Akkurat som med for-løkker er den beste måten å forstå while-løkker å eksperimentere litt. Hvis du ønsker å skrive ut tallene fra 5 til 12 så kan du modifisere koden over slik:

i = 5
while i < 13:
    print(i)
    i += 1

Dersom du heller har lyst å skrive ut tallene multiplisert med seg selv så endrer du koden i kodeblokken:

i = 1
while i < 8:
    print(i * i)
    i += 1

På samme måte som med for-løkker så spiller det ingen rolle for Python hva du kaller variabelen i. Hvis anledningen byr seg, så bruk gjerne et mer passende navn enn i.

En ting du kanskje merker deg er at i eksemplene over så vet vi jo egentlig hvor mange ganger while-løkken skal kjøre. Eksempel som dette demonstrerer hvordan man skriver while-løkker, men ikke hvorfor while-løkker er nyttige. I alle eksemplene over ville jeg nok heller brukt en for-løkke. La oss gjøre et eksempel der while-løkker virkelig får vist seg fram.

5.4.2 Tampen brenner

Spillet tampen brenner går som følger: Spillmesteren har på forhånd valgt et tall mellom 1 og 100. Spilleren skal prøve å gjette dette tallet. Hvis tallet spilleren gjetter er for stort får spilleren beskjeden for varmt!. Hvis tallet er for lite, får spilleren beskjeden for kaldt!. Spilleren kan deretter gjette på nytt helt til han/hun treffer riktig tall. Målet er å finne riktig tall så raskt som mulig. La oss tenke litt over situasjonen før vi skriver ut koden:

  • Vi trenger å først lage et tall mellom 1 og 100 som er tallet spilleren prøver å finne.

  • Gjetteleken går flere runder, der spilleren kommer nærmere og nærmere det riktige tallet. Så her trenger vi en løkke der hver gjetterunde er en iterasjon.

  • Vi vet IKKE hvor mange runder spilleren vil bruke på å finne riktig tall. Dette avhenger av hva spilleren gjetter først, og hvor lur spilleren er, og hva som er det riktige tallet. Derfor er en while-løkke perfekt.

Koden under gir en løsning på problemet:

riktig_tall = 75
gjett = int(input('Gjett et tall fra og med 1 til og med 100? '))
while gjett != riktig_tall:
    if gjett > riktig_tall:
        print('For varmt!')
    elif gjett < riktig_tall:
        print('For kaldt!')
    gjett = int(input('Gjett igjen på et tall fra og med 1 til og med 100? '))
print('Riktig!')

Merk at vi må først be om input fra spilleren over while-løkken slik at vi kan evaluere sannhetsuttrykket i toppen av while-løkken. Inni while-løkken sjekker vi om gjettet er for høyt eller for lavt. Vi ber spilleren om et nytt gjett i slutten av kodeblokken til while-løkken. Hvorfor det? Etter at kodeblokken til while-løkken er ferdig vil betingelsen gjett != riktig_tall på toppen av while-løkken bli evaluert igjen. Da er det viktig at spilleren har fått gjettet igjen før denne betingelsen blir evaluert.

Hvis du kjører koden over kan du spille tampen brenner. Du får late som du ikke har sett hva riktig_tall ble satt til. Det er juks å være både spillmester og spiller!

5.4.3 Uendelige løkker

Et litt skummelt konsept som kan oppstå når vi skriver while-løkker er uendelige løkker53. Dette er en løkke som gjentar seg om og om igjen uten å stoppe. Dette er fordi sannhetsuttrykket som blir evaluert med hver iterasjon aldri tar verdien False. Her er et eksempel på en uendelig løkke:

i = 1
while i < 8:
    print(i)

Koden over ser ganske uskyldig ut. Problemet er at vi aldri oppdaterer variabelen i inni while-løkken. Dermed vil uttrykket i < 8 alltid evalueres til True, og løkken vil fortsette for alltid. Kjører du programmet over så vil det bare skrive ut tallet 1 om og om igjen og aldri stoppe.

Uendelige løkker kan være ekstra skummelt for nye programmerere, men det er bare en feil som alle andre. Du kan avbryte et kjørende program ved å skrive Ctrl + C i terminalen hvis du har Windows eller Cmd + C i terminalen hvis du har Mac. Hvis alt annet feiler kan du alltids starte IDE-programmet ditt på nytt.

Det kan være fort gjort å skrive en uendelig løkke. Prøv å tenke gjennom hva som vil skje når du skriver en while-løkke. Koden over ser veldig lik ut som en while-løkke som stopper rent visuelt, men logikken fungerer ikke. Du setter først i = 1. Deretter, så lenge i < 8, så printer du ut variabelen i. Dette vil bli en uendelig løkke fordi i aldri blir større.

Uendelige løkker kan virke som en feil med programmeringsspråket Python der noen burde stilles til ansvar. Men uendelige løkker kan iblant være nyttig i mer avanserte applikasjoner. Hvis du skriver et program som lytter på innsendte meldinger fra et annet system, og deretter behandler dem når de kommer fram, så kan dette skrives som en uendelig løkke. Programmet kan da ha en form som under:

while True:  # Lytt for innkommende meldinger
    # Ta imot meldinger
    # Behandle meldinger
    # Send meldinger videre, eller gjør noe annet med dem

I prosjektoppgaven i kapittel 6 skal vi skrive en uendelig løkke. Da vil du se at uendelige løkker kan avbrytes av en bruker. Så selv om uendelige løkker ofte er et tegn på at du har skrevet en løkke feil så er det betryggende å vite at de har sin plass. Programmeringsspråket Python er heldigvis godt gjennomtenkt.

5.5 Break for å bryte ut av en løkke

Så langt har vi studert løkker der vi har latt løkken bli ferdig av seg selv. Dette er nyttig i noen tilfeller, mens andre ganger må vi instruere løkken til å enten:

  • Bryte ut av løkken hvis en betingelse er sann. For å gjøre dette kan vi bruke nøkkelordet break.

  • Hoppe over en iterasjon hvis en betingelse er sann. For å gjøre dette kan vi bruke nøkkelordet continue.

I denne seksjonen ser vi på hvordan nøkkelordet break fungerer og hvor det er hensiktsmessig å bruke. I neste seksjon skal vi ta for oss nøkkelordet continue på samme måte.

Vi bruker som sagt nøkkelordet break for å avslutte en løkke tidlig. Et enkelt eksempel på dette er som følger:

siste_tall = 0
for tall in range(1, 8):
    siste_tall = tall
    if tall == 5:
        break
print(f'Vi er nå ferdig med løkken. Det siste tallet var {siste_tall}.')

Når vi i koden over kommer til iterasjonen der tall får verdien 5, så utløses nøkkelordet break. Da blir hele resten av løkken hoppet over. Koden her illustrerer hvordan vi kan bruke break sammen med en betinget setning til å avslutte en løkke tidlig. Eksempelet er likevel litt kunstig siden det er helt åpenbart at vi ønsker å stoppe løkken etter fem iterasjoner. La oss heller se på et annet eksempel der break virkelig kommer til unnsetning.

Vi skal skrive kode som har en liste med navn personer og et spesifikt navn ønsket_person. Vi ønsker å finne posisjonen til det første elementet i listen personer som er lik navnet lagret i ønsket_person. La oss først løse dette uten å bruke nøkkelordet break, og deretter se hva break kan bidra med.

personer = ['Eirik', 'Stine', 'Theo']
ønsket_person = 'Stine'
for indeks in range(len(personer)):
    if personer[indeks] == ønsket_person:
        print(f'Posisjonen er {indeks}.')

Vi har her brukt en vanlig for-løkke og ikke en for-hver-løkke siden vi trenger informasjon om indeksen. Koden oppfører seg tilsynelatende slik den skal. Gjør den egentlig det? Hva skjer hvis det ønskede navnet lagret i ønsket_person er gjentatt flere ganger i listen personer?

personer = ['Eirik', 'Stine', 'Theo', 'Stine']
ønsket_person = 'Stine'
for indeks in range(len(personer)):
    if personer[indeks] == ønsket_person:
        print(f'Posisjonen er {indeks}.')

Dette fungerte ikke like bra. Her blir to indekser skrevet ut, mens målet var å bare skrive ut indeksen til det første treffet på ønsket_person i listen personer. Som du sikkert har forstått så er det nettopp nøkkelordet break vi må bruke her. Vi ønsker å avslutte løkken ved første treff på ønsket_person, slik at det ikke skrives ut flere ganger.

personer = ['Eirik', 'Stine', 'Theo', 'Stine']
ønsket_person = 'Stine'
for indeks in range(len(personer)):
    if personer[indeks] == ønsket_person:
        print(f'Posisjonen er {indeks}.')
        break

Funksjonaliteten til koden vi har laget over er veldig lik listemetoden .index(). Listemetoder er veldig nyttig siden vi da ikke trenger å skrive den mest vanlige funksjonaliteten på nytt. Likevel er det betryggende at vi kan skrive dette ut selv om vi ønsker. Dette gir oss en fleksibilitet slik at vi ikke blir totalt avhengig av listemetoder, men kan bruke dem på samme måte som man bruker en kalkulator til å legge sammen 113 og 294. Det er lettvint med en kalkulator, men vi hadde fått det til selv hvis det var nødvendig.

5.6 Continue for å gjøre ferdig en iterasjon

Vi bruker nøkkelordet continue for å hoppe over resten av iterasjonen i en løkke. Et enkelt eksempel på dette er som følger:

for tall in range(1, 8):
    if tall == 5:
        continue
    print(tall)

I koden over så skriver vi først ut alle tallene fra 1 til 4. Når tall har verdien 5 bruker vi nøkkelordet continue som hopper over resten av kodeblokken i den iterasjonen. Da starter vi direkte på neste iterasjon når tall er lik 6 og fortsetter som normalt.

Nøkkelordet continue er mindre drastisk enn nøkkelordet break. Nøkkelordet break bryter ut av hele løkken, mens nøkkelordet continue bare hopper over nåværende iterasjon.

Sett at vi har en liste med navn personer og ønsker å skrive ut alle navnene i listen utenom et spesifikt navn uønsket_person. Dette er perfekt for å bruke nøkkelordet continue siden vi kan hoppe over iterasjonene der uønsket_person dukker opp:

personer = ['Eirik', 'Stine', 'Theo', 'Stine']
uønsket_person = 'Stine'
for navn in personer:
    if navn == uønsket_person:
        continue
    print(navn)

Det er mulig å unngå nøkkelordet continue her om ønskelig. Du kan nemlig skrive:

personer = ['Eirik', 'Stine', 'Theo', 'Stine']
uønsket_person = 'Stine'
for navn in personer:
    if navn != uønsket_person:
        print(navn)

De to løsningene over er etter min mening like gode. Er dermed nøkkelordet continue unødvendig siden man likeså godt kan skrive kode uten? Eksempler som dette har fått flere til å se skeptisk på nøkkelordet continue. Jeg skal nå gi et eksempel som opprettholder continue sitt gode navn og rykte. Eksempelet viser nemlig at nøkkelordet continue kan noen ganger gjøre kode betydelig enklere.

5.6.1 Unngå mange horisontale nivåer

Et undervurdert bruksområde til nøkkelordet continue er til å unngå mange horisontale nivåer i koden din. Les koden under og se om du forstår hva den gjør:

telefonnumre = [
    '+47 95483256', 
    '+46 14372216', 
    '+47 468503578', 
    '+47 63831148'
]

for nummer in telefonnumre:
    if len(nummer) == 12:
        if nummer[:3] == '+47':
            if nummer[4] in ['4', '9']:
                print(f'Nummeret {nummer} er et gyldig mobilnummer.')
            else:
                print(f'Nummeret {nummer} begynner ikke på 4 eller 9.')
        else:
            print(f'Nummeret {nummer} har ikke landskode +47.')
    else:
        print(f'Nummeret {nummer} har ikke riktig lengde.')

Som du sikkert har forstått så sjekker koden forskjellige telefonnumre for å se om de er gyldige. Et irritasjonsmoment med koden er de mange horisontale nivåene. Dette gjør koden vanskeligere å både lese og utvide. Vi kan bruke nøkkelordet continue til å omskrive koden som vist under:

telefonnumre = [
    '+47 95483256', 
    '+46 14372216', 
    '+47 468503578', 
    '+47 63831148'
]

for nummer in telefonnumre:
    if len(nummer) != 12:
        print(f'Nummeret {nummer} har ikke riktig lengde.')
        continue
    if nummer[:3] != '+47':
        print(f'Nummeret {nummer} har ikke landskode +47.')
        continue
    if nummer[4] not in ['4', '9']:
        print(f'Nummeret {nummer} begynner ikke på 4 eller 9.')
        continue
    print(f'Nummeret {nummer} er et gyldig mobilnummer.')

Her velger vi heller å avslutte iterasjonen med en gang noe er galt. På denne måten har vi brettet ut koden slik at den er flatere og ikke går så dypt. Dersom vi trenger å legge til flere sjekker på telefonnumrene så kan vi nå gjøre dette uten at vi får enda flere horisontale nivåer.

5.7 Prosjekt - Verdens mest brukte intervjuspørsmål

I dette prosjektet skal vi sammen løse et av verdens vanligste intervjuspørsmål for nye utviklere i Python. Målet er ikke å memorere svaret, men å kunne løse lignende spørsmål og oppgaver med enkelhet.

5.7.1 Oppgaven

Vi skal løse det klassiske intervjuspørsmålet som heter FizzBuzz. Her er en beskrivelse av intervjuspørsmålet.

For alle tallene mellom 1 og 100, gjør følgende:

  • Dersom tallet er delelig på 3, skriv ut 'Fizz'.

  • Dersom tallet er delelig på 5, skriv ut 'Buzz'.

  • Dersom tallet er delelig på både 3 og 5, skriv ut 'FizzBuzz'.

  • Ellers skal du bare skrive ut tallet i seg selv.

5.7.2 Forberedelse

Før vi hopper inn i koden er det lurt å identifisere hva som trengs for å løse et slikt problem. Litt tenking identifiserer noen hovedkomponenter:

  • Vi trenger å gå gjennom tallene fra 1 til 100. Til dette er en for-løkke nyttig siden vi vet på forhånd hvilke tall vi skal gå gjennom.

  • Vi trenger en betinget setning med strukturen if-elif-elif-else siden vi har 4 forskjellige muligheter som kan inntreffe for hvert tall.

  • Vi må greie å sjekke om et tall er delelig på 3, 5, eller begge to. I seksjon 1.4 lærte vi at modulo-operatoren % kan hjelpe oss med dette.

5.7.3 Løse et enklere problem først

Vi løser et enklere problem først, og deretter bygger videre på dette. La oss først skrive ut tallene fra 1 til 20 der vi bytter ut tall delelige på 3 med strengen 'Fizz'. Prøv å skrive dette ut før du ser på løsningen under:

for tall in range(1, 21):
    if tall % 3 == 0:
        print('Fizz')
    else:
        print(tall)

Vi ser at betingelsen tall % 3 == 0 sjekker om tallet er delelig på 3. Dette gikk jo supert! Vi kan nå prøve å utvide logikken til å dekke de andre tilfellene. Den mest naturlige måten å gjøre dette på er å legge inn to elif-setninger som følger:

for tall in range(1, 21):
    if tall % 3 == 0:
        print('Fizz')
    elif tall % 5 == 0:
        print('Buzz')
    elif tall % 3 == 0 and tall % 5 == 0:
        print('FizzBuzz')
    else:
        print(tall)

Her bruker vi nøkkelordet and for å sjekke om et tall er både delelig på 3 og 5. Selv om koden over ser riktig ut, så viser utskriften en helt annen historie. Når tall er lik 15 burde det bli skrevet ut 'FizzBuzz' og ikke 'Fizz'. Hva er galt?

5.7.4 Et problem med logikken vår

Det er helt naturlig å støte på problemer når vi programmerer. Iblant er problemene symbolske feil som feilstavede variabelnavn eller å glemme å avslutte en streng med symbolet ' eller ". Slike symbolske feil er som regel enkle å rette opp. Andre ganger er feilen av mer logisk art. I vårt tilfelle er Python-koden vi har skrevet helt gyldig. Problemet er at den logisk ikke løser det som var hensikten. La oss se hva som er galt.

Alt er riktig fram til når tall er lik 15. Da burde vi skrevet ut 'FizzBuzz', men skrev bare ut 'Fizz'. Vi kan se hvorfor ved å lese gjennom koden. Det første vi spør er om betingelsen tall % 3 == 0 er sann. Det er den jo, siden 15 er delelig på 3. Da skriver vi ut 'Fizz' og dermed sjekkes ikke de neste betingelsene.

Problemet er at grenene i den betingede setningen ikke er strukturert i riktig rekkefølge. Den betingelsen som er mest restriktiv må først, ellers ender vi opp med å hoppe inn i feil betingelse. Med den nye forståelsen av den logiske feilen vår kan vi løse intervjuspørsmålet.

5.7.5 Endelig løsning

Vi kan nå skrive ned den endelige løsningen. Prøv å rett opp i den logiske feilen vi støtte på før du ser på løsningen under:

for tall in range(1, 101):
    if tall % 3 == 0 and tall % 5 == 0:
        print('FizzBuzz')
    elif tall % 3 == 0:
        print('Fizz')
    elif tall % 5 == 0:
        print('Buzz')
    else:
        print(tall)

Etter å ha gått gjennom dette intervjuspørsmålet sakte ser du kanskje også hvorfor det er så populært å bruke. Det tester både bruk av løkker og betingede setninger. I tillegg vil de fleste som ikke har sett oppgaven før bli fristet til å gjøre den samme logiske feilen som vi støtte på. En god intervjuer bryr seg ikke så mye om kandidaten gjør feil, men mer om kandidaten greier å finne ut av feilen når den først har oppstått. Å rette sine egne feil på en strukturert måte er ekstremt verdifullt for å bli en dyktig programmerer.

Ikke fortvil hvis du synes dette er vanskelig. Hvis det er en ting du kommer til å få mye mengdetrening av når du programmerer, så er det å gjøre feil og deretter fikse dem.

5.8 Oppgaver

Oppgave 1

Hva gjør følgende kode? Beskriv hvert steg!

riktig_passord = 'hemmelig'
passord_forsøk = input('Gjett passordet: ')

while passord_forsøk != riktig_passord:
    passord_forsøk = input('Prøv igjen, eller skriv A for å avslutte: ')

    if passord_forsøk.upper() == 'A':
        print('Du har valgt å avslutte programmet.')
        break
else:
    print('Riktig passord. Veldig bra!')

Prøv å forstå hvilken rolle else-setningen har her. Du finner en forklaring på else-setninger sammen med løkker i oppgave 4 under.

Oppgave 2

Hva er summen av alle partallene fra 2 til og med 100? Altså, hva får vi når vi legger sammen partallene:

\[2 + 4 + 6 + \dots + 100 = \, ?\]

  • Skriv en for-løkke som regner ut summen av alle partallene fra 2 til og med 100.

  • Skriv en while-løkke som regner ut summen av alle partallene fra 2 til og med 100.

Tips: Husk at du kan bruke modulusoperatoren % for å sjekke om et tall er et partall.

Oppgave 3

Du har fått en liste over telefonnumrene til hemmelige agenter:

telefonnumre = ['94567844', '83429122', '76890544', '11223344', '99887766']

Skriv en for-hver-løkke med en if-setning som går gjennom listen telefonnumre og skriver ut en melding til alle numrene som slutter på tallet 44. Meldingen kan eksempelvis se slik ut for den første agenten:

Agent med telefonnummer 94567844, du skal ut på et hemmelig oppdrag!

Oppgave 4

Etter en for-løkke eller en while-løkke kan du legge på en else-setning. Denne else-setningen blir utført hvis løkken fullfører uten å støte på nøkkelordet break. Her er et eksempel:

personer = ['Eirik', 'Stine', 'Theo', 'Stine']
ønsket_person = 'Kamila'
for indeks in range(len(personer)):
    if personer[indeks] == ønsket_person:
        print(f'Posisjonen er {indeks}.')
        break
else:
    print('Personen finnes ikke i listen!')

Dette kalles for en for-else-løkke54. På samme måte kan vi legge til en else-setning til en while-løkke og få en while-else-løkke55.

Her er oppgaven din: I koden under bruker vi en variabel er_primtall for å finne ut om tallet mistenkt_primtall er et primtall. Kan du skrive om koden slik at du bruker en for-else-løkke i stedet for variabelen er_primtall? Da slipper vi å bruke ekstra variabler.

mistenkt_primtall = int(input('Skriv inn et tall du tror er et primtall: '))
er_primtall = True
for mulig_faktor in range(2, mistenkt_primtall):
    if mistenkt_primtall % mulig_faktor == 0:
        print('Tallet er ikke et primtall.')
        er_primtall = False
        break
    
if er_primtall:
    print('Tallet er et primtall.')

Oppgave 5 (Utfordrende)

Når vi skriver for-hver-løkker, ønsker vi noen ganger å jobbe med både indeksene og elementene. En vanlig for-hver-løkke gir oss derimot bare elementene, slik som her:

pizzameny = ['Pepperoni', 'Vegetar', 'Tunfisk', 'Kylling']
for pizza in pizzameny:
    print(pizza)

Dersom elementene i listen pizzameny har en rekkefølge er denne informasjonen også nyttig å ha med. Til dette kan du bruke funksjonen enumerate() slik:

pizzameny = ['Pepperoni', 'Vegetar', 'Tunfisk', 'Kylling']
for indeks, pizza in enumerate(pizzameny):
    print(f'Pizza nummer {indeks} har navnet {pizza}.')

Som du ser, bruker du funksjonen enumerate() på en liste. Den returnerer både indeksene og elementene slik at du har tilgang til begge deler i løpet av for-hver-løkken.

Her er oppgaven din: Du er dommer i en fektekonkurranse og har fått tildelt både deltakere og poengsummene deres i to lister slik:

deltakere = ['Astrid', 'Bjørn', 'Carmen', 'David', 'Elene']
poeng = [6, 7, 7, 10, 4]

Bruk enumerate() sammen med en for-hver-løkke for å skrive ut følgende meldinger til terminalen:

Deltaker 1 med navnet Astrid fikk 6 poeng.

Deltaker 2 med navnet Bjørn fikk 7 poeng.

Deltaker 3 med navnet Carmen fikk 7 poeng.

Deltaker 4 med navnet David fikk 10 poeng.

Deltaker 5 med navnet Elene fikk 4 poeng.