Gå till innehållet

Kapitel 4 - Upprepning med loopar samt felsökning

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.

I slutet av kapitlet ska vi undersöka hur man kan använda Visual Studio för att gå igenom sin kod steg-för-steg vilket kan hjälpa när man vill hitta buggar i sina program. En annan fördel med att gå igenom koden steg-för-steg är att det kan bli lättare att förstå kod som kan kännas svår.

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 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");

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
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++;
}

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. Denna ökning sker med raden i++, operatorn ++ ökar värdet av en variabel 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 4.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 hen få en ny chans att svara på frågan.

Lösningsförslag 4.1

Uppgift 4.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 4.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;
}

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.

Vårt mål nu är att göra om programmet så att man kommer tillbaka till menyn 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 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ändarens val 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;
    }
}

Uppgift 4.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 4.3

for

Det är väldigt vanligt att man skriver loopar som ska köra ett förbestämt antal gånger. 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}");
}

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}");
}

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.

Tilldelningsoperatorer

I den första loopen i det föregående exemplet så ökades värdet av variabeln år med tilldelningsoperatorn +=. Här är en tabell med några av de tilldelningsoperator som finns.

OperatorBetydelse
+=Addition och tilldelning (ökning)
-=Subtraktion och tilldelning (minskning)
*=Multiplikation och tilldelning (multiplicering)
/=Division och tilldelning (delning)
++Ökning med 1
--Minskning med 1

Många av tilldelningsoperatorerna är förkortningar av andra uttryck som man kan skriva, t.ex. så är a += b en förkortning av a = a + b.

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 skrivs ut i varje körning av loopen.

// Räkna från 10 ner till och med 0
for (int i = 10; i >= 0; i--)
{
    Console.WriteLine($"Nedräkning! {i}");
}

Uppgift 4.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 4.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 4.4

Uppgift 4.5

Skapa ett program som skriver ut vart 5:e årtal på 1400-talet med början på 1495 och sedan nedåt, det vill säga 1495, 1490, 1485 och så vidare ända till 1400.

Lösningstips 4.5

Låt räknarvariabeln börja på 1495 och minska den med 5 varje körning, d.v.s. låt det inte stå i++ i slutet utan istället i -= 5. Använd gärna forr tillsammans med Intellisense för att låta Visual Studio ge dig skalet till en nedåträknande for-loop.

Lösningsförslag 4.5

Typerna char och bool

Hittills så har vi sett att string används för att spara text och att int samt double används för att spara tal. Vi ska nu titta på en variabeltyp som används när man vill spara ett enskilt tecken, denna variabeltyp kallas char från de första bokstäverna i character, det engelska ordet för tecken. Man kan spara ett enskilt tecken inuti en string också, men det finns skillnader i hur en string och en char lagras i minnet och därför så används char för en del ändåmål ändå. Vi ska med några exempel se hur man använder char och hur man kan gå igenom en sträng tecken för tecken med hjälp av en loop.

Först så tittar vi på ett exempel på hur man kan hämta ett tecken från en sträng och spara det i en egen char-variabel.

string text = "ABC123";

// Tecknet på index 0 = A
char tecken1 = text[0];

// Tecknet på index 4 = 2
char tecken2 = text[4];

// Skapa ett eget tecken direkt i koden
char egetTecken = 'S';

// Skriv ut de tecken du har
Console.WriteLine($"{tecken1} {tecken2} {egetTecken}");

// För att omvandla ett tecken till ett tal så gör du enklast om det till en sträng först
string tecken2String = tecken2.ToString();
// Omvandla därefter strängen till ett tal
int tecken2Tal = int.Parse(tecken2String);
// Nu går talet att räkna med
Console.WriteLine(tecken2Tal + 5);

Man hämtar ett tecken från en sträng genom att skriva namnet på strängen följt av ett index inuti hakparenteser. Ett index är ett tal som anger i vilken ordning ett visst tecken befinner sig i en sträng. Indexen i en sträng börjar alltid på 0 så det första tecknet har index 0, det andra har index 1 och så vidare. I exemplet visas också hur man skapar en char-variabel direkt i koden. Notera att man använder enkla citationstecken, t.ex. 'S' när man definierar en char medan dubbla citationstecken används för strängar som vi har sett tidigare.

I exemplet ovan så visas också en omväg man kan använda ifall man vill omvandla ett tecken till en int som sedan går att räkna med. Kommandot int.Parse fungerar bara när man använder det med en sträng. För att omvandla ett tecken till ett tal så omvandlar vi det först till en sträng med hjälp av ToString() och därefter till en int.

Som ytterligare ett 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 när 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 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]);
}

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.

Vi avslutar detta avsnitt med att introducera ytterligare en ny variabeltyp: bool. En bool-variabel används när man vill spara ett värde som är sant eller falskt, vilket anges med de två orden true och false, dessa två värden är de enda värden som en bool-variabel kan ha. De är användbara när man vill spara information som bara kan vara just sann eller falsk, till exempel om det finns minst ett litet a inuti en sträng, vilket vi ska se i följande exempel.

Console.WriteLine("Skriv in lite text");
string text = Console.ReadLine();

char tecken = 'a';
// Vi förutsätter att texten inte innehåller ett litet a tills vi hittar ett
bool textInnehållerA = false;

for (int i = 0; i < text.Length; i++)
{
    if (text[i] == tecken)
    {
        textInnehållerA = true;
    }
}

// Om texten innehåller minst ett 'a' så är variabeln true
if (textInnehållerA)
{
    Console.WriteLine("Din text innehåller minst ett 'a'");
}
else
{
    Console.WriteLine("Din text innehåller INTE något 'a'");
}

När man vill ta reda på om någonting finns minst en gång så kan det vara bra att börja med att anta att det man letar efter inte finns för att sedan ändra en lämplig variabel när man hittar det. Exemplets kod är skriven på detta sätt, bool-variabeln ändras endast till true om loopen hittar ett litet a inuti strängen. När loopen är klar undersöker en if-sats om bool-variabeln är true eller false. Eftersom en if-sats körs om uttrycket i parentesen är true så behöver vi inte undersöka variabeln med '='tecken, det räcker med att skriva namnet på variabeln direkt.

Uppgift 4.6

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

Lösningsförslag 4.6

Uppgift 4.7

Skapa ett program som beräknar siffersumman av ett tal som användaren skriver in. Siffersumman fås om man beräknar summan av varje siffra i ett tal, t.ex. så är siffersumman av 527 lika med 14 eftersom 5 + 2 + 7 = 14.

Lösningstips 4.7

Läs in användarens tal som en sträng. Gå igenom hela strängen med en loop och omvandla varje tecken till ett tal för att kunna beräkna siffersumman.

Lösningsförslag 4.7

Uppgift 4.8

Skapa ett program som ber användaren skriva in ett tal. Berätta för användaren om någon av siffrorna 3 eller 7 fanns i talet. Du ska lösa denna uppgift utan att använda Contains.

Lösningstips 4.8

Läs in användarens tal som en sträng. Gå igenom hela strängen med en loop och använd en bool-variabel för att hålla koll på om det finns minst en 3:a eller 7:a.

Lösningsförslag 4.8

do while

Det finns en loop i C# som är väldigt lik while-loopen men skrivs på ett lite 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 om 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);

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 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}");
}            

Ä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 4.9

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 4.8

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 4.9

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}");
}

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 4.10

Skapa ett program som skriver ut alla tal från 1 till 30 förutom 12 och 27. Utnyttja ett continue någonstans i din loop.

Lösningsförslag 4.10

Uppgift 4.11

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 4.11

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 4.11

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.

Innan vi kollar på koden med de nästlade looparna ska vi se på en annan metod för utmatning som kommer att användas i dessa exempel, nämligen Console.Write. Till skillnad från Console.WriteLine så går programmet inte vidare till nästa rad när man skriver ut något med Console.Write. Detta betyder att man kan göra flera utskrifter på samma rad genom att använda upprepade Console.Write. Testa gärna att skapa ett testprogram med följande kod för att se hur Console.Write fungerar innan du går vidare till exemplet med nästlade loopar.

Console.Write($"Text 1 ");
Console.Write($"Text 2 ");
Console.Write($"Text 3 ");

När du känner dig säker på hur Console.Write fungerar så kan du gå vidare till följande exempel med nästlade loopar.

// Här körs det som senare är den inre loopen
for (int j = 0; j < 5; j++)
{
    Console.Write($"j={j}    ");
}
// Avsluta raden
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();
}

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 tills 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 upprepar 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 som ofta utnyttjar nästlade loopar.

I programmeringsproblem av högre svårighetsgrad så är det väldigt vanligt att man behöver använda nästlade loopar för att lösa det problem man har, ofta så kan man behöva använda fler än två nivåer av loopar. Vi ska avsluta avsnittet med att se på hur man kan lösa en sådan problemlösningsuppgift av högre svårighetsgrad. Problemet vi ska lösa är:

"Låt användaren skriva in två ord. Undersök om alla tecken i det första ordet finns på minst två platser i det andra ordet."

Detta skulle t.ex. vara sant för orden abc och aabcbccc, men inte för ab och aab.

Detta är en typisk uppgift som kräver användning av nästlade loopar. Vi behöver gå igenom varje tecken i den första strängen med en loop, och för varje tecken så måste vi sedan gå igenom den andra strängen med en till loop. En påbörjan av lösningen kan se ut så här:

Console.WriteLine("Skriv in ett ord");
string ord1 = Console.ReadLine();
Console.WriteLine("Skriv in ett ord till");
string ord2 = Console.ReadLine();

bool finnsAllaTeckenTvåGånger = true;
// Gå igenom varje tecken i det första ordet
for (int i = 0; i < ord1.Length; i++)
{

}

Programmet läser in orden som behövs och går sedan igenom varje tecken i det första ordet för att se om det finns i det andra ordet. Så hur kan vi se om ett visst tecken finns i det andra ordet? Vi får gå igenom hela det andra ordet tecken för tecken med hjälp av en loop och jämföra det med det tecken i det första ordet som vi är på just nu.

Console.WriteLine("Skriv in ett ord");
string ord1 = Console.ReadLine();
Console.WriteLine("Skriv in ett ord till");
string ord2 = Console.ReadLine();

bool finnsAllaTeckenTvåGånger = true;
// Gå igenom varje tecken i det första ordet
for (int i = 0; i < ord1.Length; i++)
{
    // Kolla om tecknet på index i finns i det andra ordet
    int antal = 0;
    for (int j = 0; j < ord2.Length; j++)
    {
        if (ord1[i] == ord2[j])
        {
            // Hit kommer vi om tecknet finns.
            antal++;
        }
    }
    if (antal < 2)
    {                   
        finnsAllaTeckenTvåGånger = false;
        // Om ett tecken inte finns minst två gånger kan vi avsluta loopen
        break;
    }
}

if (finnsAllaTeckenTvåGånger)
{
    Console.WriteLine("Alla tecken i det första ordet finns minst två gånger i det andra ordet");
}
else
{
    Console.WriteLine("Åtminstone ett tecken i det första ordet finns inte minst två gånger i det andra ordet");
}

Även om nästlade loopar är en liten del av detta kapitel så kan du förvänta dig att de flesta av de allra svåraste uppgifterna i detta och kommande kapitel kommer att använda nästlade loopar på något sätt, ibland med mer än två nivåer.

Uppgift 4.12

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 4.12

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

Lösningsförslag 4.12

Uppgift 4.13

Skapa ett program som skriver ut en ihålig rektangel bestående av X. Användaren ska bestämma rektangelns bredd och höjd. Som exempel så ska en rektangel med bredden 7 och höjden 5 se ut så här:

XXXXXXX
X     X
X     X
X     X
XXXXXXX
Lösningstips 4.13

Använd nästlade loopar för att gå igenom varje rad/kolumn i rektangeln. Skriv ut X på första/sista raden och kolumnen, annars skriver du ut ett mellanslag.

Lösningsförslag 4.13

Uppgift 4.14

Skapa ett program där användaren får skriva in ett ord. Programmet ska berätta om något av tecknen som finns i ordet endast förekommer en gång.

Lösningstips 4.14

Gå igenom varje tecken i ordet med en loop. Gå sedan igenom varje tecken i ordet en gång till med en inre loop och undersök om tecknet på index i är detsamma som tecknet på index j, om du har i och j som räknarvariabler.

Lösningsförslag 4.14

Thread.Sleep

Vi ska i detta avsnitt titta på något 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 nästa rad.

// 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.

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.

Uppgift 4.15

Utöka exemplet så att det pausar lika lång tid vid kommatecken, frågetecken och utropstecken som det gör 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 4.15

Uppgift 4.16

Utöka exemplet 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 4.16

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 4.16

Olika typer av fel

Du har vid det här laget säkert haft problem med att ditt program någon gång inte vill starta eller att det inte gör som du vill. Detta beror alltid på att programmet har någon typ av fel, och vi ska nu studera några av de fel som kan uppstå för att bättre veta hur man ska slippa dem. Man brukar dela in de fel som ett program kan ha i följande tre typer: kompileringsfel, exekveringsfel och logiska fel.

Kompileringsfel

Ett kompileringsfel gör så att ditt program inte kan kompileras, d.v.s. att din kod inte kan omvandlas till ett program. Om din kod har ett kompileringsfel så kommer Visual Studio att rödmarkera det ställe i koden där felet finns och programmet kan inte startas. Kompileringsfel kallas också för syntaxfel, och exempel på sådana som du säkert har stött på är att man kan glömma att skriva ett semikolon i slutet av en rad eller en parentes på ett ställe där det hade behövts.

Titta på raden innan

Ibland så rödmarkerar Visual Studio en rad som ser helt korrekt ut. Ibland blir en rad rödmarkerad när den föregående raden har något fel, t.ex. att den saknar ett semikolon. Om du inte hittar felet på den rödmarkerade raden kan det därför vara en bra idé att titta på den föregående.

Exekveringsfel

Ett exekveringsfel får programmet att krascha när det kör, t.ex. genom att programmet försöker omvandla text som inte innehåller tal med hjälp av int.Parse. Andra exempel på exekveringsfel är när programmet försöker komma åt ett index som inte finns i en sträng eller utföra division med 0.

Här är några av av de vanligaste exekveringsfelen som man kan stöta på som nybliven programmerare och hur man kan göra för att undvika dem. I C# så kallas exekveringsfel också för exceptions vilket översätts till undantag på svenska.

FormatException

När man försöker omvandla en sträng till ett tal med int.Parse så kommer programmet att krascha med ett FormatException om strängen inte kan omvandlas till ett tal. Detta felet är vanligt när användaren ska skriva in ett tal men hen råkar skriva in något som innehåller bokstäver. Du kan undvika de flesta FormatExceptions i dina program genom att använda int.TryParse istället för int.Parse, int.TryParse visas i ett senare kapitel så fram tills dess får du helt enkelt försöka skriva in tal när programmet frågar efter det.

IndexOutOfRangeException

När ett program försöker komma åt ett index som inte finns inuti en sträng så kommer programmet att krascha med IndexOutOfRangeException. Indexet som du försöker komma åt är antingen för litet (om det är mindre än 0) eller för stort (om det är större än eller lika med strängens längd). Om du får ett IndexOutOfRangeException inuti en for-loop så får du tänka till lite extra om vilka värden loopens räknarvariabel har som minst och störst.

Ett IndexOutOfRangeException kan också uppstå om användaren skriver in det index som man vill komma åt i en array, då är det viktigt att undersöka det tal användaren skrev in så att det inte är för stort eller litet. Du kan göra denna undersökning med en if-sats.

Felhantering

Det går att göra så att ett program inte kraschar när ett exekveringsfel uppstår även om vi inte tar upp hur man gör detta i denna bok. Man måste i sådana fall definiera hur man ska hantera felet, vilket kallas att man fångar upp felet. Detta görs med två typer av block som heter try och catch.

Logiska fel

Logiska fel är fel som innebär att programmet inte fungerar så som det är tänkt trots att det går att starta och att det inte kraschar. Denna typ av fel är ofta den svåraste typen att lösa. Ett exempel på ett logiskt fel skulle kunna vara att man vill att ett program ska räkna ut medelvärdet av två tal men att det inte beräknar medelvärdet korrekt när medelvärdet ska vara ett decimaltal. Logiska fel beror på att programmeraren helt enkelt har skrivit fel kod för att lösa det problem som hen ville.

Även om logiska fel är svåra att upptäcka så kan Visual Studio hjälpa oss att hitta dem. Vi gör det genom att gå igenom vår kod rad för rad för att upptäcka vad som händer med programmets variabler, och du ska nu få lära dig hur man kan stega sig igenom programmet på detta sätt.

Uppgift 4.17

Skapa ett program som ger ett IndexOutOfRangeException när det körs, användaren ska inte behöva skriva in någonting.

Lösningsförslag 4.17

Uppgift 4.18

Skapa ett program som ger ett FormatException när det körs, användaren ska inte behöva skriva in någonting.

Lösningsförslag 4.18

Felsökning med debuggern

När du kör dina program i Visual Studio så brukar du antagligen trycka på knappen ”Start” i verktygsraden högst upp eller tangenten F5. Det som egentligen händer när man gör detta är att man startar programmet med hjälp av Visual Studios debugger, ett engelskt ord som skulle kunna översättas till avbuggare eller buggborttagare. Vi ska se hur man kan använda debuggern för att gå igenom sitt program rad för rad.

För att testa debuggern så behöver vi program som vi ska köra igenom steg för steg. För att se hur man kan använda debuggern så ska vi titta på följande program som räknar ut summan av talen 0 till 4 med hjälp av en loop.

Console.WriteLine("Detta program har en loop");
Console.WriteLine("Loopen räknar ut summan av talen 0 till 4");
int summa = 0;

for (int i = 0; i < 5; i++)
{
    Console.WriteLine("Nu skrivs värdet av i ut");
    Console.WriteLine($"i = {i}");
    // Öka summa-variabeln med värdet av i
    summa += i;
}

Console.WriteLine($"Summan av talen är {summa}");

Breakpoints

Det finns olika sätt att pausa programmet med hjälp av debuggern, ett av de vanligaste är att sätta en breakpoint eller brytpunkt på en kodrad. Programmet kommer då att stanna precis innan denna rad ska köras. Du kan sätta en breakpoint genom att placera textmarkören på en rad och trycka på tangenten F9. Det går också bra att placera textmarkören på en rad och sedan välja Debug -> Toggle Breakpoint i Visual Studios meny. Du tar bort en breakpoint med samma knapp som du skapar den. Ytterligare ett sätt att skapa en breakpoint är att trycka med musen i det grå området till vänster om radens radnummer.

För att börja undersöka vårt program med debuggern så skapar vi en breakpoint på raden som skriver ut ”Loopen räknar ut summan av talen 0 till 4”. Denna rad kommer att bli rödmarkerad och en rund röd stoppikon visas längst bort till vänster på raden så som bilden visar. Kom ihåg att du kan även skapa eller ta bort en breakpoint genom att trycka med musen på den plats där stoppikonen visas.

Breakpoints i Visual Studio

När programmet är pausat så ändras verktygsraden lite och ser ut som nedanstående bild visar.

Menyrad för debuggern, 1

Knappen ”Start” har ändrats till att visa ”Continue”. Genom att trycka på denna knapp så fortsätter programmet att köra tills det kommer till någon ny breakpoint, om det inte finns några mer breakpoints i programmet så kommer det att köra klart precis som vanligt. Det finns en röd fyrkantig stoppknapp för att stänga av programmet och precis till höger om den finns en knapp som startar om programmet från början. Ännu längre bort till höger finns en knapp som du kan använda tillsammans med debuggern för att stega sig igenom koden rad för rad, denna har markerats i bilden nedanför.

Menyrad för debuggern, 2

Denna knapp heter Step Into. Du hittar även samma kommande i menyn Debug i Visual Studios menyrad. Knappen Step Into, som ser ut som en pil som pekar neråt på en punkt, kör den kodrad som programmet är på just nu och pausar därefter direkt innan nästa rad körs. Vill du gå igenom allt som händer i ditt program så är det Step Into som du ska använda. Du kan använda tangenten F11 som kortkommando istället för att trycka på knappen med musen.

Notera att fönstret Locals visas nere till vänster när du har pausat ditt program med debuggern. Om du inte ser Locals-fönstret så tar du fram det via Debug -> Windows -> Locals i menyn. Här kan du se värdet av alla lokala variabler som finns i den metod som du kör just nu. Du kan också hålla muspekaren över en variabel i det vanliga kodfönstret för att se vad deras värden är just vid det ögonblick som programmet är pausat.

Undersöka variabelvärden

Det är väldigt användbart att undersöka variabelvärden när programmet har pausat vid en breakpoint eller på något annat sätt. Ett annat sätt att se värdet av en variabel när man testar sina program är att skriva ut det med Console.WriteLine men nu slipper du lägga till och ta bort dessa när du felsöker ett program.

Ett alternativ till att skapa en breakpoint för att pausa programmet på ett visst ställe är att högerklicka på raden som du vill pausa innan och välja Run To Cursor. Programmet kommer då att köras till den raden som du markerade eller till eventuella breakpoints som finns tidigare i programmet. När programmet är pausat så kan du välja en ny rad, högerklicka på den och sedan välja Run To Cursor igen för att programmet ska köra vidare till den raden. Testa att ta bort breakpointen i exempelprogrammet och stega igenom det med Run To Cursor.

Du ska nu, med hjälp av samma program som i exemplet ovan, träna på att stega igenom ett program med breakpoints, Continue-knappen, Step into och Run to cursor i några uppgifter. För smidighets skull så visas här programkoden som du ska kopiera in i ett program när du gör uppgifterna.

Console.WriteLine("Detta program har en loop");
Console.WriteLine("Loopen räknar ut summan av talen 0 till 4");
int summa = 0;

for (int i = 0; i < 5; i++)
{
    Console.WriteLine("Nu skrivs värdet av i ut");
    Console.WriteLine($"i = {i}");
    // Öka summa-variabeln med värdet av i
    summa += i;
}

Console.WriteLine($"Summan av talen är {summa}");

Uppgift 4.19

Använd exempelkoden ovan och gör så att programmet innehåller en breakpoint på raden som skriver ut Detta program har en loop och en annan breakpoint på raden Console.WriteLine($"i = {i}");. Kör igenom programmet och tryck på Continue varje gång det stannar. När stannar programmet och vad syns i programfönstret då?

Uppgift 4.20

Använd exempelkoden ovan och gör så att programmet inte innehåller några breakpoints. Gå igenom programmet men pausa precis innan raden som skriver ut Detta program har en loop med hjälp av Run to cursor. Pausa därefter programmet precis innan raden Console.WriteLine($"Summan av talen är {summa}"); ska köras genom att återigen använda Run to cursor.

Uppgift 4.21

Använd exempelkoden ovan och gör så att programmet endast innehåller en breakpoint på radenConsole.WriteLine("Loopen räknar ut summan av talen 0 till 4");. Gå därefter igenom resten av programmet genom att använda kanppen Step into och titta hela tiden på fönstret Locals i Visual Studio för att se hur variabelvärdena ändras när programmet kör.

Använd debuggern när du har problem med uppgifter

Försök att använda debuggern med breakpoints, run to cursor och step into när du fastnar på kodningsuppgifter. Det är bra att träna på hur debuggern används så inför ett senare kapitel om MonoGame.

Blandade uppgifter till kapitel 4

Uppgift 4.22

Skapa ett program som skriver ut talen 10, 11, 12 o.s.v. upp till och med 30. Därefter ska programmet skriva ut talen 200, 199, 198 o.s.v. ner till och med 180. Slutligen ska programmet skriva ut talen 1000, 1050, 1100, 1150 o.s.v. upp till och med 1400.

Lösningsförslag 4.22

Uppgift 4.23

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 fler tal ska programmet skriva ut vilket som var det största talet som användaren skrev in.

Lösningstips 4.23

Skapa en variabel som du kallar störst. Ge denna variabel värdet av det första talet som användaren skriver in. Jämför alla kommande tal med det värdet som variabeln har och ändra den ifall det behövs.

Lösningsförslag 4.23

Uppgift 4.24

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.

För att undersöka om ett tal är delbart med ett annat så använder man modulo-operatorn som skrivs som ett procent-tecken. För att undersöka om t.ex. i är delbart med 3 så ska uttrycket i % 3 == 0 vara sant, och detta kan du undersöka med en if-sats.

Lösningsförslag 4.24

Uppgift 4.25

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 4.25

Uppgift 4.26

Låt användaren skriva in ett heltal. Skriv ut samma tal 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 4.26

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 4.26

Uppgift 4.27

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 4.27

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

Lösningsförslag 4.27

Uppgift 4.28

Skapa ett program där användaren får skriva in en sträng. Därefter ska programmet skriva in en sträng med exakt två tecken. Programmet ska undersöka om båda dessa tecken finns i den första strängen som användaren skrev in. Exempelvis så ska programmet säga att båda tecknen finns om användaren först skriver in aabbcc och sedan ac.

Lösningstips 4.28

Använd en bool-variabel per tecken för att hålla koll på om tecknet finns i den första strängen.

Lösningsförslag 4.28

Uppgift 4.29

Skapa ett program som ritar ut en rätvinklig triangel där användaren får bestämma triangelns sidlängd. Om användaren till exempel anger sidlängden 5 så ska följande triangel ritas ut:

*
**
***
****
*****

Lösningsförslag 4.29

Uppgift 4.30

Skapa ett program som skriver ut grupper av "X" och "O" så att de bildar ett mönster. Användaren ska själv få välja hur många X och O som ska finnas i varje grupp, hur många "O-grupper" som visas per rad samt hur många rader som ska skrivas ut.

Tryck på följande länk för att se en exempelkörning av programmet för att lättare förstå vad programmet ska göra, se sedan till att ditt program ger samma utskrift med den inmatning som visas i exemplet.

Lösningsförslag 4.30

Uppgift 4.31

Skapa ett program där användaren skriver in ett ord. Programmet ska skriva ut alla tecken som förekommer mer än 1 gång i ordet, men varje sådant tecken ska bara skrivas ut en gång. Om användaren till exempel skriver in ordet ananas så ska programmet skriva ut.

'a' förekommer mer än 1 gång.
'n' förekommer mer än 1 gång.

Om inget tecken förekommer mer än 1 gång, t.ex. för ordet mat så ska programmet i stället skriva ut

Inget tecken förekommer mer än 1 gång

Lösningsförslag 4.31

Uppgift 4.32

Skapa ett program som skriver ut ett "rutnät" av kvadrater. Användaren ska själv få välja kvadraternas sidlängd samt bredd och höjd på rutnätet.

Tryck på följande länk för att se en exempelkörning av programmet för att lättare förstå vad programmet ska göra, se sedan till att ditt program ger samma utskrift med den inmatning som visas i exemplet.

Lösningsförslag 4.32

Uppgift 4.33

Skapa ett program där användaren skriver in två ord på var sin rad. Programmet ska skriva ut alla tecken som förekommer fler gånger i det första ordet än i det andra, tecknen ska skrivas ut på samma rad. Om användaren till exempel skriver in aaabcccdd följt av abbbcd så ska programmet skriva ut

acd

Om inga tecken i det första ordet förekommer fler gånger än i det andra så behöver programmet inte skriva ut något.

Lösningsförslag 4.33

Fler svåra uppgifter

Fler svåra uppgifter från Programmeringsolympiaden

Varje år genomförs Programmeringsolympiaden, också kallat gymnasie-SM i programmering. En del av deras kvaluppgifter är lämpliga som svåra uppgifter att träna på. Uppgifterna som ingår i deras online-kval rättas dessutom automatiskt om du laddar upp din kodfil.

Du hittar en lista över tidigare problem på https://www.progolymp.se/traning/uppgifter/

Lämpliga uppgifter efter detta kapitel är Namnsdag och Färgrobot från onlinekvalet 2016.