Gå till innehållet

Kapitel 5 - Upprepning med loopar

Introduktion till kapitlet

Våra program i de tidigare kapitlen har kört koden i programmet uppifrån och ner, från början och till slut, och därefter har de avslutats. Vi ska nu se hur vi kan låta kod upprepas om och om igen med hjälp av loopar. Möjligheten att låta kod köras flera gånger kommer bland annat låta oss skapa en ny typ av program som vi kommer att kalla för menyprogram där användaren hela tiden får välja vad hen vill göra, programmet kommer inte att avslutas förrän användaren själv väljer det. Kapitlet avslutas med att se hur man kan låta programmets körning pausa en valfri tid innan det går vidare till nästa rad vilket kan kombineras med loopar för att få intressanta effekter.

Loopar på svenska

På svenska kallas loopar ibland för slingor.

while

Den första och enklaste loopen som vi ska studera kallas för en while-loop. En while-loop är ganska lik en if-sats, den innehåller ett villkor som behöver vara sant för att while-blocket ska köras. Skillnaden mot en if-sats är att efter att en koden i ett while-block har körts så kommer while-loopens villkor att undersökas igen. Om while-loopens villkor fortfarande är sant så kommer koden i while-blocket att köras igen. Detta fortsätter att ske efter varje körning av while-loopen, den är alltså en upprepande if-sats. Vi undersöker hur while-loopen fungerar med hjälp av ett exempel.

Console.WriteLine("Skriv in ett ord");
Console.WriteLine("Skriv ordet 'Klar' för att avsluta");

string användarensOrd = Console.ReadLine();

while (användarensOrd != "Klar")
{
    Console.WriteLine($"Du skrev in ordet {användarensOrd}");
    Console.WriteLine("Skriv in ett ord till");
    Console.WriteLine("Skriv ordet 'Klar' för att avsluta");
    användarensOrd = Console.ReadLine();
}

Console.WriteLine("Du skrev ordet 'Klar', programmet kommer att avslutas");
Console.ReadKey();

I detta exempel så kommer användaren att få fortsätta att skriva in ord ända till hen skriver in ordet ”Klar”. Villkoret i while-loopen undersöks alltså innan loopen körs första gången och därefter innan varje upprepad körning av loopen. Om det hade funnits någon kodrad inuti while-loopen efter raden användarensOrd = Console.ReadLine(); så hade den körts även om man skriver in ordet ”Klar”, det är inte förrän while-loopen ska starta om som villkoret undersöks igen.

Intellisense-tips

Visual Studio kan hjälpa dig att skriva while-loopar snabbt om du skriver while och sedan trycker på tab-tangenten två gånger.

I det första exemplet så användes en while-loop för att köras tills användaren skrev in ordet ”Klar” så antalet körningar av loopen är okänt tills programmet körs. Det går även att skriva en while-loop som ska köra ett förbestämt antal gånger om man använder sig av en variabel som får fungera som en räknare, den räknar helt enkelt hur många gånger loopen har körts. Av tradition så brukar en räknarvariabel döpas till namnet i. I exempel nedan så används räknarvariabeln tillsammans med en while-loop för att köra koden i while-loopen 15 gånger.

// Kör loopen 15 gånger, i
int i = 1;
while (i <= 15)
{
    // Variabeln i håller koll vilken körning vi är på
    Console.WriteLine($"Detta är körning nummer {i}");
    i++;
}

Console.ReadKey();

Räknarvariabeln i ges från början värdet 1. Loopen i programmet kommer att forsätta köras så länge som i har ett värde som är 15 eller mindre. För att loopen inte ska köra i all oändlighet betyder detta att vi måste ändra värdet av i så att det någon gång blir större än 15, annars kommer loopen aldrig att avslutas. Det som händer vid varje körning av loopen är att värdet av i skrivs ut för att du ska kunna se hur detta värde ändras och i slutet av loopen så ökar värdet på i med 1. Eftersom värdet ökar med 1 varje körning kommer i ha värdet 1 den första körningen, 2 den andra körningen, 3 den tredje körningen och så vidare. Därför fungerar i som en räknare som anger vilken körning som loopen är inne på. Efter den 15:e körningen kommer i att öka till värdet 16 och då kommer loopen inte att starta om när körningen är klar.

Vad kan man skriva mellan parenteserna efter while?

Parenteserna efter while ska innehålla ett booleskt uttryckt precis som en if-sats. Allt som du skulle kunna skriva i en if-sats kan du också skriva i en while-loop.

Uppgift 5.1

Skriv ett program som ställer en fråga till användaren, t.ex. ”Vilket är världens folkrikaste land?”. Så länge som användaren svarar fel ska hon få en ny chans att svara på frågan.

Lösningsförslag 5.1

Uppgift 5.2

Skapa ett program som skriver ut talen 50 till 1 med hjälp av en while-loop. Ett tal ska skrivas ut per rad, 50 ska skrivas på första raden och 1 på den sista.

Lösningsförslag 5.2

Menyprogram med while

Med hjälp av while-loopen ska vi nu skapa ett program som vi kommer att kalla för menyprogram. Ett menyprogram visar en meny för användaren där hen får välja vad hen vill göra i programmet genom att skriva in en siffra. Menyn skulle till exempel kunna se ut så här.

Välj ett av följande alternativ.
1. Addera två tal
2. Multiplicera två tal
3. Avsluta programmet

Att göra ett program med flera alternativ på detta sätt är inget som kräver loopar om användaren bara ska kunna göra ett av valen och därefter låta programmet avslutas, men det är inte detta som vi i denna bok menar med ett menyprogram. Ett menyprogram ska, efter att användaren har gjort ett val och utfört det som valet medföljer, komma tillbaka till menyn igen och låta användaren göra ett nytt val ända till hen väljer alternativet ”Avsluta programmet”.

För enkelhetens skull börjar vi att se hur grunden til ett menyprogram som har de alternativ som visades innan skulle kunna se ut. Detta gör vi genom att göra menyn där användaren får göra ett val, men efter att användarens val har utförts så avslutar vi programmet. För detta behöver vi inte använda några loopar.

Console.WriteLine("Välj ett av följande alternativ.");
Console.WriteLine("1. Addera två tal");
Console.WriteLine("2. Multiplicera två tal");

string användarensVal = Console.ReadLine();

// Utför det val som användaren ville göra
switch (användarensVal)
{
    case "1":
        Console.WriteLine("Skriv in två tal på var sin rad");
        double tal1 = double.Parse(Console.ReadLine());
        double tal2 = double.Parse(Console.ReadLine());
        Console.WriteLine($"{tal1}+{tal2}={tal1 + tal2}");
        break;

    case "2":
        Console.WriteLine("Skriv in två tal på var sin rad");
        double faktor1 = double.Parse(Console.ReadLine());
        double faktor2 = double.Parse(Console.ReadLine());
        Console.WriteLine($"{faktor1}*{faktor2}={faktor1 * faktor2}");
        break;

    default:
        Console.WriteLine("Du valde inte ett giltigt alternativ.");
        break;
}

// Avsluta inte direkt
Console.ReadKey();

Användaren får skriva in ett av två alternativ. Om hen skriver in en 1:a får hen sedan skriva in två tal och deras summa skrivs ut, om hen istället skriver en 2:a får hen skriva in två tal och deras produkt beräknas istället. Om användaren skriver in något annat än en 1:a eller 2:a så anger programmet att man inte valde ett giltigt alternativ. Efter att man har gått igenom något av dessa tre alternativ så avslutas programmet när användaren trycker på en tangent.

Vårt mål nu är att göra om programmet så att man kommer tillbaka till meny igen när ett av valen har utförts. Det gör vi genom att lägga i princip hela programmet inuti en while-loop, eftersom all kod som man vill ska upprepas ska måste ligga i en loop. Menyn får nu ett nytt alternativ som avslutar programmet, alternativ 3. Eftersom programmet ska avslutas om användaren väljer alternativ 3 innebär det att loopen ska köra så länge som användarens val inte är 3, och detta blir det som while-loopen undersöker. Eftersom loopen ska undersöka variabeln som innehåller användarensval så måste denna deklareras innan loopen.

string användarensVal = "";
Console.WriteLine("Detta är ett menyprogram");
Console.WriteLine("Det kan användas för att addera och multiplicera tal");

// Visa menyn om och om igen så länge användaren inte väljer "3"
while (användarensVal != "3")
{
    // Tom rad för tydlighetens skull
    Console.WriteLine();

    Console.WriteLine("Välj ett av följande alternativ.");
    Console.WriteLine("1. Addera två tal");
    Console.WriteLine("2. Multiplicera två tal");
    Console.WriteLine("3. Avsluta programmet");

    användarensVal = Console.ReadLine();

    // Utför det val som användaren ville göra
    switch (användarensVal)
    {
        case "1":
            Console.WriteLine("Skriv in två tal på var sin rad");
            double tal1 = double.Parse(Console.ReadLine());
            double tal2 = double.Parse(Console.ReadLine());
            Console.WriteLine($"{tal1}+{tal2}={tal1+tal2}");
            break;

        case "2":
            Console.WriteLine("Skriv in två tal på var sin rad");
            double faktor1 = double.Parse(Console.ReadLine());
            double faktor2 = double.Parse(Console.ReadLine());
            Console.WriteLine($"{faktor1}*{faktor2}={faktor1*faktor2}");
            break;

        case "3":
            Console.WriteLine("Programmet kommer avslutas...");
            break;

        default:
            Console.WriteLine("Du valde inte ett giltigt alternativ.");
            break;
    }
}

Notera att vi nu kan ta bort raden Console.ReadKey(); eftersom programmet ändå inte kommer att avslutas om inte användaren själv väljer att göra detta genom att skriva en trea. Utskriften Console.WriteLine("Programmet kommer avslutas...") kommer därför inte att hinna visas för användaren så som programmet är skrivet nu.

Uppgift 5.3

Skapa ett menyprogram som innehåller följande alternativ.

Välj ett av följande alternativ.
1. Omvandla meter till kilometer
2. Omvandla kilometer till meter
3. Avsluta programmet

Lösningsförslag 5.3

for

Det är väldigt vanligt att man skriver loopar som ska köra ett förbestämt antal gånger likt det föregående exemplet med en while-loop. För att åstadkomma detta med en while-loop så behövde vi se till att skapa en räknarvariabel innan while-loopen samt se till att räknarens värde ökar i slutet av loopen. Eftersom loopar med räknare är så vanliga så finns det en annan typ av loop som brukar användas när man vill köra loopen ett bestämt antal gånger, denna loop heter for-loopen.

for-loopen liknar while-loopen till utseendet men skiljer sig genom att den har tre uttryck inuti sina parenteser istället för att endast ha ett.

Det första av for-loopens tre uttryck kommer att köras exakt en gång innan loopens första körning och där brukar man ofta deklarera och tilldela en räknarvariabel ett värde. Det andra uttrycket ska vara ett booleskt uttryck och det är detta villkor som måste vara sant för att loopen ska köras, detta uttryck är alltså samma sorts uttryck som while-loopen och en if-sats har. Det tredje uttrycket är ett uttryck som kommer att köras efter varje körning av loopen, här är det vanligt att man ändrar värdet av räknarvariabeln. Nedan följer ett exempel med en for-loop som gör i princip samma sak som det förra exemplet med en while-loop som hade en räknarvariabel. Skillnaden är att räknarvariabeln från början får värdet 0 istället för 1, det är vanligt att man börjar med värdet 0 när man använder en for-loop och anledningen till detta kommer bli tydlig i kapitlet som handlar om arrayer.

// Kör loopen 15 gånger, i börjar på 0 och slutar på 14
for (int i = 0; i < 15; i++)
{
    Console.WriteLine($"Denna körning har i värdet {i}");
}

Console.ReadKey();

Loopen i detta exempel är den vanligaste användningen av en for-loop: den innehåller en räknarvariabel som heter i som börjar på 0 och ökar med 1 varje körning av loopen. Talet 15 anger antalet körningar som loopen kommer att göra, den sista körningen kommer värdet av i att vara 14. Eftersom denna typ av for-loop är så vanlig så har Visual Studio ett snabbt sätt att skapa den: skriv ordet for och tryck därefter på tab-tangenten två gånger. Du får då en tom for-loop där du endast behöver fylla i antalet körningar loopen ska göra istället för ordet length, du kan till och med trycka på tab-tangenten en gång till för att komma fram till ordet length och ändra det.

Intellisense-tips

Visual Studio kan hjälpa dig att skriva for-loopar snabbt om du skriver for och sedan trycker på tab-tangenten två gånger. Du kan därefter använda tab-tangenten för att bläddra mellan de värden som for-loopen ska ha.

Om du vill ha en for-loop som räknar nedåt istället för uppåt så kan du skriva forr och sedan trycka på tab-tangenten två gånger, forr står för for reversed.

Även om det tidigare exemplet visar på det vanligaste användningsområdet för en for-loop så måste den inte användas på detta sätt. Här visas ett exempel med två olika for-loopar som ger precis samma utskrift fastän de är skrivna på olika sätt. Syftet med loopen är att skriva ut det första årtalet i varje decennium under 2000-talet, d.v.s. 2000, 2010, 2020 ... 2080, 2090. Den första loopen gör detta genom att låta årtalet vara variabeln som ändras och undersöks i loopen, den andra loopen använder istället en räknarvariabel som börjar på 0 och använder denna variabel för att räkna ut årtalet.

Console.WriteLine("Decennierna på 2000-talet börjar på åren...");
// Loop som börjar på 2000 och ökar loop-variabeln med 10 varje gång
for (int år = 2000; år < 2100; år += 10)
{
    Console.WriteLine($"... {år}");
}

Console.WriteLine();
Console.WriteLine("Här kommer dessa årtal igen... ");
// Loop som börjar på 0 och ökar loop-variablen med 1 varje gång
for (int i = 0; i < 10; i++)
{
    int år = 2000 + i * 10;
    Console.WriteLine($"... {år}");
}

Console.ReadKey();

Som du ser när du testkör programmet så ger båda looparna precis samma utskrift och det är inte så att den ena loopen är bättre eller sämre än den andra, vilken loop man föredrar är en smaksak. Värt att notera i detta exempel är att man inte hade behövt kalla variabeln i den första loopen för år, den hade också kunnat heta i precis som variabeln i den andra loopen. En variabel som deklareras i det första uttrycket i en for-loop finns bara tillgänglig inuti for-loopen, när for-loopen har kört färdigt så finns den inte längre och då är namnet ledigt att användas igen i t.ex. en ny for-loop. För tydlighetens skull så döptes dock loop-variabeln i den första for-loopen till år eftersom det namnet bättre beskriver variabelns innehåll.

I de exempel vi har kollat på hittills så har vi använt räknarvariabler som ökat varje körning, men ibland så vill ha en räknarvariabel som minskar också. Det följande exemplet visar på en for-loop med en minskande räknarvariabel som visar värdet av resten när i delas med 4.

// Skriv ut resten vid heltalsdivision av talen 10 - 0 med talet 4
for (int i = 10; i >= 0; i--)
{
    Console.WriteLine($"Resten av {i} delat med 4 är {i % 4}");
}

Console.ReadKey();

Som ett sista exempel på en for-loop så ska vi se hur man kan använda räknarvariabeln för att komma åt enskilda tecken i en sträng. Kom ihåg att om meddelande är en sträng så kommer man åt det första tecknet i strängen med meddelande[0], det andra tecknet med meddelande[1] o.s.v. Dessutom så kan man få reda på längden av strängen med meddelande.Length, längden är alltså ett mer än det indexet för det sista tecknet.

string meddelande = "Ett vertikalt hej!";

// Skriv ut ett tecken från meddelandet per rad
for (int i = 0; i < meddelande.Length; i++)
{
    Console.WriteLine(meddelande[i]);
}

Console.ReadKey();

Den första gången loopen körs är värdet av i noll och då kommer alltså det första tecknet i strängen att skrivas ut på en egen rad. Nästa körning av loopen så har i värdet ett och då kommer det andra tecknet i strängen att skrivas ut på en egen rad. Resultatet av alla loopens körningar blir att strängen meddelande skrivs ut vertikalt iställlet för horisontellt. Här ser vi en fördel med att räknarvariabeln börjar på 0 istället för 1 och det är att index för det första tecknet i en sträng är 0.

Uppgift 5.4

Skapa ett program som skriver ut talen 40 till 80 med hjälp av en for-loop. Ett tal ska skrivas ut per rad, 40 ska skrivas på första raden och 80 på den sista.

Lösningstips 5.4

Låt räknarvariabeln börja på 40 och kör loopen så länge som den är mindre än 81.

Lösningsförslag 5.4

Uppgift 5.5

Skapa ett program som skriver ut vart 5:e årtal på 1400-talet, det vill säga 1400, 1405, 1410 och så vidare ända till 1495.

Lösningstips 5.5

Låt räknarvariabeln börja på 1400 och öka den med 5 varje körning, d.v.s. låt det inte stå i++ i slutet utan istället i += 5.

Lösningsförslag 5.5

Uppgift 5.6

Skapa ett program som skriver ut ett meddelande vertikalt, meddelandet ska dessutom skrivas ut baklänges.

Lösningsförslag 5.6

do while

Det finns en loop i C# som är väldigt lik while-loopen men skrivs på ett litet annorlunda sätt, denna loop kallas för ”do while”-loopen. Skillnaden mellan while och do while är att while-loopen undersöker loopens villkor innan varje körning av loopen medan do while undersöker loopens villkor efter varje körning av loopen. Detta leder till att en do while-loop alltid körs minst en gång oavsett om dess villkor är falskt eller sant från början, loopen körs en gång och loopens villkor är sant efter körningen så börjar den om och gör en ny körning.

int i = 4;
do
{
    Console.WriteLine($"i={i}");
    i++;
} while (i > 4 && i < 10);

Console.ReadKey();

En do while-loop brukar användas när man inte vet hur många gånger loopen behöver köras men man vill ända vara säker på att den kommer att köras minst en gång. Exemplet som användes för do while-loopen visar alltså inte dess vanliga användning men det är gjort för att tydligt visa hur en do while-loop fungerar. I de flesta fall så brukar det vara enkelt att skriva om en do while-loop till en vanlig while-loop som fungerar på samma sätt och while-loopen har fördelen att man direkt ser vad den har för villkor som måste vara uppfyllt för att den ska upprepas till skillnad från do while-loopen där man inte kan se det förrän efter man har läst all kod i loopens block. Eftersom do while-loopen har denna nackdel så kommer den inte att användas mer i denna bok men den har visats här för att du ska få se de loopar som finns i C#.

Vilken loop ska man använda?

De tre olika looparna som vi har undersökt har olika användningsområden även om det är möjligt att använda vilken loop man vill för att lösa vilket problem som helst, dock kan koden bli lite krångligare och mer svårläst om man använder en loop som inte är bäst lämpad för det man vill göra. I tabellen visas de vanliga användningarna för de tre looptyper som detta kapitel behandlar och även en fjärde loop-typ, foreach (for each, på svenska för varje) som introduceras i kapitlet om arrayer.

Typ av loop Används när man ...
while ... inte vet hur många gånger loopen behöver köras.
for ... vet hur många gånger loopen behöver köras.
do while ... inte vet hur många gånger loopen behöver köras men man vill vara säker på att den körs minst en gång.
foreach ... vill göra någonting för varje element i en array, lista eller annan samling.

I exemplet nedan visas tre olika loopar som exempel på när looptyperna while och for kan användas.

// Använd while om du inte vet hur många gånger loopen ska köras
string användarensOrd = "j";
while (användarensOrd != "n")
{
    Console.WriteLine("Skriv in ett ord. Avsluta genom att skriva in 'n'.");
    användarensOrd = Console.ReadLine();
    Console.WriteLine($"Du skrev in ordet {användarensOrd}");
    Console.WriteLine();
}

// Använd for om du vet hur många gånger loopen ska köras
for (int i = 0; i < 5; i++)
{
    Console.WriteLine($"Denna körning av for-loopen är i = {i}");
}
Console.WriteLine();

// Även här "vet" vi antalet körningar
Console.WriteLine("Hur många gånger vill du köra den sista for-loopen?");
int antalKörningar = int.Parse(Console.ReadLine());
for (int i = 0; i < antalKörningar; i++)
{
    Console.WriteLine($"Denna körning av for-loopen är i = {i}");
}            

Console.ReadKey();

Även om man inte känner till hur många gånger den sista loopen kommer att köras när man skriver koden eftersom det beror på vilket tal användaren skriver in så kommer programmet att veta det när det är dags att köra loopen och därför används en for-loop och inte en while-loop.

Uppgift 5.7

Skapa ett program där användaren får välja hur många kvadrattal som ska skrivas ut med start på 1. Ett kvadrattal är ett tal man får när man tar ett heltal upphöjt till två. Om användaren t.ex. väljer att programmet ska skriva ut 4 kvadrattal så ska programmet skriva ut

1*1 = 1
2*2 = 4
3*3 = 9
4*4 = 16

Lösningstips 5.7

Eftersom du vet hur många gånger loopen ska köra efter att användaren har matat in detta så är en for-loop lämplig i detta program.

Lösningsförslag 5.7

break och continue

Det finns ytterligare sätt att kontrollera körningarna av en loop och nu ska vi kolla på två av dessa: break och continue. Genom att skriva break i en loop så avbryts loopen och kommer inte att fortsätta köras någon gång mer oavsett om loopens villkor är sant eller inte. Ordet continue avbryter den nuvarande körningen av loopen och går genast vidare till nästa körning. I följande exempel så får du se både continue och break användas.

for (int i = 0; i < 20; i++)
{
    // Hoppa över körningarna när i är 3 eller 7
    if (i == 3 || i == 7)
    {
        continue;
    }
    // Avbryt loopen helt när i är 14
    else if (i == 14)
    {
        break;
    }

    Console.WriteLine($"i = {i}");
}

Console.ReadKey();

Om räknarvariabeln i är 3 eller 7 så kommer raden med continue att köras och då avslutas den nuvarande körningen av loopen. Detta innebär att anropet till Console.WriteLine("i = " + i) inte kommer att göras när i är 3 eller 7 eftersom denna rad kommer senare i loopen. När i är 14 så kommer loopen att avbrytas helt med break och detta innebär att i aldrig kommer att vara något av talen från 15 till 19 även om loopen egentligen skulle ha körts med dessa värden.

break i loopar och switch

Samma nyckelord, break, används både för att avsluta ett case i en switch-sats och en loop. När du använder break eller continue så är det alltid den innersta eller "närmaste" loopen eller caset som avbryts.

Uppgift 5.8

Skapa ett program som skriver ut alla tal från 1 till 30 som inte är delbara med 4. Utnyttja ett continue någonstans i din loop.

Ett tal är delbart med 4 om resten vid division med 4 är 0, så ett tal är därmed inte delbart med 4 om resten vid division med 4 blir något annat än 0.

Lösningstips 5.8

Det kan vara lämpligt att använda continue i loopen om det nuvarande värdet av räknarvariabeln är delbart med 4.

Lösningsförslag 5.8

Uppgift 5.9

Skapa ett program där användaren får svara på frågan ”Vilket är Europas folkrikaste land?”. Användaren får maximalt 5 chanser på sig att svara. När användaren svarar rätt så ska programmet skriva ut att rätt svar angavs, därefter ska det avslutas. Utnyttja ett break någonstans i din loop.

Lösningstips 5.9

Ett sätt att lösa uppgiften är att använda en while-loop som kör tills användaren skriver rätt svar. Utöver denna loop så skapar du en egen räknarvariabel som du ökar med 1 i slutet av while-loopen. Undersök värdet av räknarvariabeln i while-loopen och om det är för högt så avbryter du loopen med break.

Lösningsförslag 5.9

Nästlade loopar

Det är möjligt att placera en loop inuti en annan loop, en loop som ligger inuti en annan loop kallas för en nästlad loop. Man brukar kalla den nästlade loopen, d.v.s. den loop som är inuti en annan loop, för den inre loopen och loopen som innehåller den inre loopen kallas för den yttre loopen. Nästlade loopar är väldigt användbara för att t.ex. arbeta med tvådimensionella saker även om de kan vara svåra att förstå till en början. Vi ska undersöka nästlade loopar med några exempel.

// Här körs det som senare är den inre loopen
for (int j = 0; j < 5; j++)
{
    Console.Write($"j={j}    ");
}
Console.WriteLine();

// En extra tom rad innan nästa del av programmet
Console.WriteLine();

// Varje körning av den yttre loopen skriver ut en hel rad
for (int i = 0; i < 3; i++)
{
    for (int j = 0; j < 5; j++)
    {
        Console.Write($"i={i}, j={j}    ");
    }
    // Avsluta raden
    Console.WriteLine();
}

Console.ReadKey();

Detta exempel består av två delar. Den första delen består endast av en loop som skriver ut talen 0 till 4 på samma rad när programmet körs. Räknarvariabeln har döpts till j istället för i som man brukar använda, det är på grund av att en variant av denna loop senare kommer att användas som inre loop, och när man arbetar med nästlade loopar med räknarvariabler brukar den yttre loopen använda i och den inre använda j.

Vi ska nu undersöka den andra delen av programmet, den som innehåller en nästlad loop. Om vi studerar den yttre loopens körvillkor ser vi att den använder sig av en räknarvariabel som heter i samt att den yttre loopen kommer att köras tre gånger, variabeln i kommer då ha värdena 0, 1 och 2. Den yttre loopen innehåller dels den inre loopen och dels att anrop till Console.WriteLine(). Den inre loopen innehåller ett anrop till Console.Write som skriver ut det nuvarande värdet av i och j samt några mellanrum för läslighetens skull.

Vi stegar igenom programmet för att förstå vad som händer. När den yttre loopen börjar sin första körning så har i värdet 0, hela loopen kommer att köras färdigt med detta värde på i innan den yttre loopen börjar om. Direkt så börjar den inre loopen sin första körning då värdet på j är 0 och detta skrivs ut till användaren. Eftersom Console.Write används istället för Console.WriteLine så kommer nästa körning av den inre loopen att skrivas ut på samma rad. När den inre loopen startar om på sin andra körning så har värdet av j ändrats till 1 men värdet av i är fortfarande 0, värdet på i kommer inte att ändras förrän den yttre loopen börjar om på sin nästa körning. Nästa körning av den inre loopen så har j blivit 2 men i är fortfarande 0, detta fortsätter till j är 4 och i är 0. När den inre loopen har körts klart med j-värdet 4 så är den helt klar och programmet går vidare till nästa rad som gör en radbrytning med Console.WriteLine(), detta innebär att kommande text i programmet kommer att skrivas ut på en ny rad. Den yttre loopen börjar nu om på sin andra körning med i-värdet 1 och upprepa alla körningar av den inre loopen som redan har gjorts, dock denna gång med i-värdet 1 istället för 0.

Mer tvådimensionella saker

I kapitlet om arrayer kommer du också att lära dig om tvådimensionella arrayer, ett tvådimensionellt sätt att spara information på som ofta utnyttjar nästlade loopar.

Som du kan se när du kör det förra exemplet så har alla utskrifter på samma rad samma värde på i och alla utskrifter i samma kolumn får samma värde på j. Detta innebär att man i detta exempel hade kunnat kalla variablerna för rad och kolumn istället för i och j, något som vi gör i det följande exemplet.

for (int rad = 0; rad < 5; rad++)
{
    for (int kolumn = 0; kolumn < 3; kolumn++)
    {
        Console.Write($"{rad},{kolumn}   ");
    }
    Console.WriteLine();
}

Console.ReadKey();

På ett ganska så likt sätt som i det förra exemplet så skrivs ett mönster av tal ut där det första talet anger vilken rad man befinner sig på, den första raden räknas som rad 0. Det andra talet anger vilken kolumn man befinner sig i om den första kolumnen räknas som kolumn 0. Detta kan jämföras med ett koordinatsystem där det första talet anger x-värdet och det andra talet anger y-värdet. Till skillnad från koordinatsystem inom matematiken så ökar y-värdena i programmet när man kollar längre ner på skärmen istället för uppåt, det är vanligt att man i datorgrafiksammanhang också använder sig av koordinater där x-värdet ökar åt höger och y-värdet ökar neråt.

Uppgift 5.10

Skapa ett program som skriver ut en tvådimensionell multiplikationstabell för talen 1 till 6. Som exempel så hade en tvådimensionell multiplikationstabell för talen 1 till 4 kunnat se ut så här.

1   2   3   4
2   4   6   8
3   6   9   12
4   8   12  16

Lösningstips 5.10

Använd nästlade loopar och beräkna produkten genom att multiplicera de båda looparnas räknarvariabler.

Utnyttja möjligheten att skriva ut text i kolumner som du fick se i kapitel 3 för att utskriften ska bli snygg.

Lösningsförslag 5.10

Uppgift 5.11

Utöka ditt program om multiplikationstabellen så att det även skriver ut en rad och kolumn med vilka tal som ska multipliceras, det skulle kunna se ut så här för en multiplikationstabell för talen 1 till 4. Gör ditt program så att det skriver ut tabellen för talen 1 till 6.

0   1   2   3   4
1   1   2   3   4
2   2   4   6   8
3   3   6   9   12
4   4   8   12  16

Lösningstips 5.11

Ändra startvärdet för looparna och använd if-satser inuti den innersta loopen för att få utskriften så som du vill.

Lösningsförslag 5.11

Thread.Sleep

Vi avslutar kapitlet med ett avsnitt som egentligen inte handlar om loopar men som kan användas tillsammans med loopar på ett lite roligt sätt. I alla program som vi har skrivit hittills så går programmet vidare till nästa kodrad så fort som den förra är klar. Man kan dock uppnå en del roliga effekter genom att säga till programmet att vänta en stund innan det ska gå vidare till nästa del av programmet och det kan man göra med metoden Thread.Sleep. När man anropar Thread.Sleep anger man i parentesen hur många millisekunder som programmet ska vänta innan det går vidare till raden som kommer efter Thread.Sleep.

Det första du måste göra i ett program som ska använda Thread.Sleep är att lägga till följande rad bland övriga rader som börjar med using längst upp i programmet:

using System.Threading;

Denna rad krävs för att kunna använda metoden Thread.Sleep utan att behöva skriva ett längre, mer komplicerat namn för att anropa den. Hittills har vi bara använt metoder som kräver de using-uttryck som Visual Studio lägger till i varje program och det är därför har vi inte behövt lägga till egna using-uttryck tidigare. Nu ska vi se på koden till själva programmet.

// Skriv ut ett nytt tal varje halvsekund (500 millisekunder = 0,5 sekunder)
for (int i = 0; i < 10; i++)
{
    Console.WriteLine(i);
    Thread.Sleep(500);
}

Programmet innehåller en loop som körs tio gånger. Varje gång loopen körs skrivs räknarvariabelns värde ut och därefter väntar programmet 500 millisekunder, d.v.s. en halv sekund, innan programmet går vidare. Effekten av detta blir att talen 0 till 9 skrivs ut med jämna tidsintervall och därefter avslutas programmet eftersom det inte innehåller någon Console.ReadKey() i slutet.

Genom att skriva om exemplet en aning så kan vi få ett program som skriver ut ett meddelande till användaren där texten inte skrivs ut direkt utan under en längre tidsperiod.

string meddelande = "Hej. Detta är ett meddelande. Tack för att du läste meddelandet.";

for (int i = 0; i < meddelande.Length; i++)
{
    Console.Write(meddelande[i]);

    // Tänk på att det är jämförelse mellan char, inte string
    if (meddelande[i] == '.')
    {
        Thread.Sleep(1000);
    }
    else
    {
        Thread.Sleep(100);
    }
}

Först så skapas det meddelande som ska skrivas ut till användaren. En for-loop används för att koden inuti for-loopen ska köras en gång per tecken i meddelandet. Kom ihåg att man får det första tecknet i strängen meddelande genom att skriva meddelande[0], det andra tecknet med meddelande[1] och så vidare. Detta innebär att den första körningen av loopen kommer att skriva ut det första tecknet i meddelandet med Console.Write(meddelande[i]) eftersom i då har värdet 0. Nästa körning av loopen kommer i ha värdet 1 och då skrivs det andra tecknet ut till användaren. Om strängen meddelande hade haft längden 10 så skulle i ha värdet 9 den sista körningen av loopen, och då skulle det sista tecknet i strängen skrivas ut eftersom det sista tecknets index är ett mindre än strängens längd.

Efter utskriften av ett tecken så väntar programmet en viss tid beroende på om tecknet som precis skrevs ut är en punkt eller inte. Om tecknet är en punkt så väntar programmet 1 sekund innan nästa tecken skrivs ut, annars så väntar programmet endast 100 millisekunder. Detta gör att meddelandet som skrivs ut ger intrycket av att programmet ”pratar” med användaren samt att det tar lite länger pauser mellan meningar. Så som programmet är skrivet nu så avslutas det när meddelandet har skrivits klart utan att vänta på att användaren ska trycka på en tangent, det är en smaksak om man vill ha det så eller inte.

Uppgift 5.12

Utöka exemplet så att det tar samma paustid vid kommatecken, frågetecken, utropstecken som vid punkter.

Använd strängen "Hej! Är 3,14 ett heltal? Tack, för att du, läste meddelandet." när du testar programmet.

Lösningsförslag 5.12

Uppgift 5.13

Utöka exempet så att programmet inte pausar längre vid decimaltal men fortfarande pausar lika länge som vid en punkt vid kommatecken som är en del av en mening.

Använd strängen "Hej! Är 3,14 ett heltal? Tack, för att du, läste meddelandet." när du testar programmet.

Lösningstips 5.13

Tänk på att ett kommatecken som är en del av en mening alltid är följt av ett mellanslag, utnyttja detta!

Lösningsförslag 5.13

Blandade uppgifter till kapitel 5

Uppgift 5.14

Skapa ett menyprogram som innehåller följande alternativ:

Välj ett av följande alternativ.
1. Subtrahera ett tal med ett annat
2. Dividera ett tal med ett annat
3. Avsluta programmet

Lösningsförslag 5.14

Uppgift 5.15

Skapa ett program där användaren ska skriva in ett heltal. Efter att hen har gjort det ska programmet fråga om användaren vill skriva in ett heltal till eller inte. Användaren ska fortsätta att få skriva in nya heltal ända tills hen inte vill göra det mer. När användaren inte vill skriva in mer tal ska programmet skriva ut vilket som var det största talet och vilket som var det minsta talet av de tal som användaren skrev in.

Lösningstips 5.15

Skapa två variabler som du kallar minst och störst. Ge dessa variabler värdet av det första talet som användaren skriver in. Jämför alla kommande tal med det värdet som variablerna har och ändra dem ifall det behövs.

Lösningsförslag 5.15

Uppgift 5.16

Skapa ett program som skriver ut talen 0 till 99. Talen 0 till 9 ska vara på samma rad, 10 – 19 ska vara på samma rad, 20 – 29 ska vara på samma rad och så vidare.

Lösningstips 5.16

Du kan lösa uppgiften med hjälp av nästlade loopar. Låt den yttre loopen hålla koll på vilket det nuvarande tiotalet är och den inre kan hålla koll på vilket ental det är.

Lösningsförslag 5.16

Uppgift 5.17

Det finns en klassisk programmeringsuppgift som kallas Fizz Buzz. Uppgiften är denna: Skapa ett program som skriver ut talen 1 till 100 på var sin rad. Men talen som är delbara med 3 ska inte skrivas ut, istället ska ordet Fizz skrivas. Talen som är delbara med 5 ska inte heller skrivas ut, skriv istället ut Buzz. Tal som är delbara med både 3 och 5 ska ersättas med FizzBuzz.

Lösningstips 5.17

Kom ihåg att om ett tal är delbart med t.ex. 3 så innebär det att resten när talet delas med 3 är 0. Du kan undersöka vilken resten blir med hjälp av operatorn %.

Lösningsförslag 5.17

Uppgift 5.18

Låt användaren skriva in ett heltal. Skriv ut samma till till användaren fast öka först alla siffror i talet med 1, nior ska ändras till nollor. T.ex. ska talet 415932 ändras till 526043.

Lösningstips 5.18

Jobba med talet i sträng-form istället för talform. Gå igenom strängen med en loop och konvertera varje enskilt tecken till ett heltal.

Lösningsförslag 5.18

Uppgift 5.19

Skapa ett program där användaren skriver in ett meddelande som ska skrivas ut vertikalt. Innan meddelandet skrivs ut så ska användaren få skriva in ett tal som anger hur många steg åt höger som det vertikala meddelande ska flyttas. Om användaren skriver in 0 så ska meddelandet skrivas ut längst till vänster i fönstret, om användaren t.ex. skriver in 3 så ska hela det vertikala meddelandet flyttas tre steg (tre mellanslag) åt höger.

Lösningstips 5.19

Det är lämpligt att lösa uppgiften med nästlade loopar.

Lösningsförslag 5.19

Uppgift 5.20

Skapa ett menyprogram som innehåller följande alternativ:

Välj ett av följande alternativ.
1. Rita ut en kvadrat i valfri storlek
2. Skriv ut ett meddelande vertikalt.
3. Avsluta programmet

Lösningsförslag 5.20

Kommentarer