Pętle

Spis treści Poprzednia strona: Instrukcja warunkowa Następna strona: Tablice

Komputery są szybkie, ale aby wykorzystać ich szybkość trzeba kazać im coś robić w kółko, powtarzać jakąś czynność, która dla ludzi jest albo uciążliwa, albo czasochłonna. Obliczenia są naturalnie jednym z najbardziej męczących zajęć dla człowieka. Rutynowe wykonywanie w kółko tych samych czynności nie jest i nie powinno być zadaniem dla ludzi. Od tego mamy maszyny. Kiedyś posiadanie kalkulatora przez ucznia w szkole było ZAKAZANE! Pozwalano na to dopiero wtedy, kiedy uczeń wykazał się zdolnością do „ręcznego” wykonywania rachunków takich jak na przykład dzielenie. Co za średniowiecze! O ileż bardziej pouczające jest zmuszenie maszyny do wykonania obliczeń za nas. Zróbmy to więc.

Dzielenie dwóch liczb całkowitych „na piechotę”

Mamy dwie dodatnie liczby całkowite a i b, przy czym 1000 > a > b > 0. Chcemy wyświetlić wynik dzielenia b przez a w postaci ułamka dziesiętnego. Będzie to liczba między 0 a 1. Wiemy, że dzielenie dwóch liczb całkowitych daje liczbę wymierną i jej ułamek dziesiętny może być albo skończony, albo nieskończony okresowy. Naszym zadaniem będzie znalezienie tego ułamka w przypadku skończonym, a w przypadku okresowym znalezienie okresu. Należy przy tym uwzględnić fakt, że okres może zaczynać się w zasadzie od dowolnego miejsca po przecinku i być w zasadzie dowolnie długi. Liczby będące okresem bierzemy w nawias podczas ich wyświetlania. Wiemy gdzie taki nawias się zaczyna dopiero wtedy, kiedy się skończy (bo wtedy kolejne cyfry zaczną się powtarzać), więc nie możemy wyświetlać kolejnych cyfr po kolei, ale musimy je gdzieś zapamiętać. Nie wnikając teraz w naturę tego pamiętania wykorzystamy obiekt zwany listą. Nie jest to może optymalne rozwiązanie, ale powinno wystarczyć jako demonstracja zarówno listy, jak i pętli, bo właśnie o pętlę tutaj chodzi.

Jak dzielimy b / a? Bierzemy liczbę a i patrzymy ile razy mieści się w liczbie b (tzn. ile razy można ją odjąć tak, żeby ciągle mieć wynik większy od zera). Oczywiście odpowiedź brzmi zawsze zero razy, bo a jest większe od b i nie zmieści się w b ani razu. Pierwszą cyfrą jest zero, następnie stawiamy przecinek, a wszystkie kolejne cyfry będą po tym przecinku. Mnożymy b przez 10 i powtarzamy tę samą czynność: sprawdzamy ile razy a mieści się w b. Tym razem wynikiem będzie jakaś cyfra od 0 do 9 włącznie. Mnożymy ją przez a i wynik odejmujemy od b. To, co dostajemy, to tzw. reszta. Dalej mnożymy resztę przez 10 i to jest nasze nowe b. Powtarzamy czynności. Kontynuujemy powtarzanie dotąd, aż reszta będzie równa zero (ułamek dziesiętny skończony) albo reszta zacznie się powtarzać, czyli wystąpi reszta, która już była (ułamek okresowy). Oto przykład:

   0,23153846 = 0,23(153846)
-------------
  301 : 1300
 -  0
 -----
  3010
 -2600
  -----
   4100
  -3900
   -----
    2000
   -1300
    -----
     7000
    -6500
     -----
      5000
     -3900
      -----
      11000
     -10400
       -----
        6000
       -5200
        -----
         8000
        -7800
         ----
          200

Po cyfrze 6 powtórzyła się reszta 200 (zaznaczona na czarno powyżej) i tam należy skończyć okres. Po raz pierwszy 200 było resztą po cyfrze 3 i tam należy zacząć okres. Skoro wiemy już na czym polega znajdowanie okresu, to piszemy program:

class UłamkiDziesiętneOkresowe
{
    static void Main(string[] args)
    {
        Console.WriteLine("Program oblicza ułamek dziesiętny");
        Console.WriteLine("dzieląc liczbę b przez liczbę a");
        Console.WriteLine("przy czym a > b > 0");
        Console.Write("Podaj b = ");
        string line = Console.ReadLine();
        int b = int.Parse(line);
        Console.Write("Podaj a = ");
        line = Console.ReadLine();
        int a = int.Parse(line);
        if (a>=1000 || b>=1000 || b >= a || 0 > a || 0 > b)
        {
            Console.WriteLine("Podano niepoprawne liczby");
            return;
        }
        Console.Write("0,"); // a dalsze cyfry po przecinku
        List<int> cyfry = new List();
        List<int> reszty = new List();
        int reszta = b, cyfra;
        do {
            reszta *= 10;
            cyfra = reszta / a;
            reszta %= a;
            cyfry.Add(cyfra);
            if (reszta == 0) { // ułamek skończony
                cyfry.ForEach(n => Console.Write(n));
                break;
            }
            else
            {
                b = reszty.FindIndex(n => n == reszta);
                if (b == 0 && cyfra == cyfry[0])
                { // okres zaczyna się po przecinku
                    Console.Write('(');
                    cyfry.GetRange(0, cyfry.Count - 1)
                        .ForEach(n => Console.Write(n));
                    Console.Write(')');
                    break;
                } else if (b >= 0) {
                    // była już taka reszta
                    // mamy ułamek okresowy
                    // okres zaczyna się
                    // od (b+1)-szej cyfry po przecinku
                    cyfry.GetRange(0, b+1)
                        .ForEach(n => Console.Write(n));
                    Console.Write('(');
                    cyfry.GetRange(b+1, cyfry.Count-b-1)
                        .ForEach(n => Console.Write(n));
                    Console.Write(')');
                    break;
                }
                else reszty.Add(reszta);
            }
        } while (true);
        Console.WriteLine();
        Console.ReadKey(true);
    }
}

Pytasz dlaczego ograniczyliśmy się do liczb mniejszych od 1000? A chcesz zobaczyć 1000 cyfr okresu wypisanych na ekranie? A miliard? (BTW możesz się nie doczekać końca tych obliczeń). Nie wierzysz? Podziel sobie 1 przez 17 i zobaczysz, że okres ma 16 cyfr. Są takie (dowolnie duże) liczby pierwsze p, że 1/p ma okres (p-1)-cyfrowy (tzw. liczby cykliczne). Duże takie liczby mają zastosowanie w generatorach liczb pseudolosowych, ale to temat na zupełnie inną bajkę.

Jaka nauka płynie z powyższego kodu? W linii 23 zaczyna się pętla do i kończy się słowami while(true) w linii 57. Polecenie break pozwala w dowolnym momencie wyjść z tej pętli. W liniach 20 i 21 mamy przykład jak się tworzy listę. W linii 27 i 55 widzimy jak się coś wstawia na koniec listy. Lista to taki worek, który rośnie w miarę wypełniania. Nie jest to worek bez dna, ale można go rozszerzać w dość dużym zakresie. W liniach 29, 39, 48 i 51 mamy tajemnicze dziwadełka. Wypisują one listę na konsolę cyfra po cyfrze w tej samej kolejności, w której te cyfry były wstawione na listę. W liniach 38, 47 i 50 bierzemy tylko fragment listy cyfr, albo przed okresem, albo sam okres. Gdyby nie warunek w linii 35 mielibyśmy 1/3 = 0,3(3) zamiast 0,(3) oraz na przykład 41/333 = 0,1(231) zamiast 0,(123).
Wreszcie w linii 34 szukamy reszty na liście zapamiętanych wcześniej reszt. Można to zrobić szybciej innym algorytmem, ale w tej sytuacji nie ma takiej potrzeby. Nie wynajmuje się ciągnika rolniczego żeby przeorać dwie grządki w ogródku, prawda?

Pętle – teoria

Pętla to sposób na to, żeby komputer powtarzał wykonywanie jakichś operacji w kółko albo 1) aż do osiągnięcia jakiegoś sukcesu,
albo 2) dopóki pewna sytuacja ma miejsce,
albo 3) z góry określoną ilość razy.
ad 1) Pętla do { instrukcje; } while (brak_sukcesu); Instrukcje są wykonywane (co najmniej raz) aż warunek logiczny „brak_sukcesu” (wyrażenie o typie bool) w nawiasie przestanie być prawdziwy (czyli zapętlamy dopóki jest prawdziwy). W powyższym przykładzie mieliśmy do { … } while (true); co oznacza, że zapętlamy się potencjalnie w nieskończoność (przynajmniej aż do odłączenia zasilania). W takim przypadku musimy wśród instrukcji gdzieś wewnątrz pętli (tzn. między słowami kluczowymi do i while) umieścić instrukcję warunkową if, żeby móc warunkowo opuścić pętlę przy pomocy instrukcji break; Mało tego: musimy mieć pewność, że zakończenie pętli jest możliwe albo przynajmniej nie jest niemożliwe.
ad 2) Pętla while (warunek) { instrukcje; } jest wykonywana dopóki warunek (prawdziwy dla danej sytuacji która właśnie ma miejsce) jest spełniony. Pętla while (w przeciwieństwie do pętli do) bada warunek zanim wykona pierwszą iterację (jedno wykonanie pętli to właśnie iteracja – zapamiętaj, bo to ważna nazwa!) dlatego jeśli warunek ten już na początku nie będzie spełniony to pętla nie wykona się ani razu.
ad 3) Pętla for (inicjalizacja_zmiennych; warunek_while; zmiana_wartości_zmiennych) { instrukcje; } jest zwykle stosowana gdy wiemy, że wykona się określoną ilość razy i w każdym kolejnym wykonaniu potrzebujemy jakieś zmiennej o innej wartości (na przykład o jeden większej, albo trzy razy mniejszej). Zmienna taka nazywana jest zmienną sterującą pętli. Zaraz podamy przykłady.

Po co aż trzy rodzaje pętli? To złe pytanie. Jest jeszcze jeden – czwarty – rodzaj: pętla foreach (typ zmienna in kolekcja) { instrukcje; } wykonuje się ona dla wszystkich wartości zmiennej, które są przechowywane w kolekcji (czyli dla każdego elementu kolekcji). Ta pętla będzie wyjaśniona później. Na razie wystarczy wiedzieć, że jest takie coś i że korzysta z interfejsów i wzorca iterator.

No to po co aż cztery rodzaje pętli? Bo każda służy do czegoś innego. To prawda, że w zasadzie wystarczyłby jeden: pętla while, ale jest ich tyle, bo każdy ma swoje zastosowania. To nie znaczy, że nie można użyć innej pętli w danym zastosowaniu. Można, ale nie powinno się! Kiedy twój kod czyta inny programista(-stka), to będzie się spodziewał(-ła) że używasz właściwej pętli do właściwego zastosowania. Ale o co chodzi? O jakie zastosowania? Już pewnie się tego domyślasz:

Pętli for używamy, kiedy z góry znamy liczbę iteracji, lub w każdej iteracji potrzebujemy zmiennej o innej przewidywalnej wartości (czyli teoretycznie łatwo da się obliczyć liczbę iteracji).

Pętli while używamy, kiedy ma być wykonywana dopóki jakiś warunek będzie spełniony i kiedy może nie być wykonana ani razu (czyli warunek należy sprawdzać najpierw).

Pętli do używamy wtedy, kiedy wiemy, że ma się ona wykonać co najmniej raz, czyli kolejne powtórzenia są opcjonalne i zależą na przykład od sukcesu lub porażki pierwszego obowiązkowego przejścia pętli (pierwszej iteracji).

Pętli foreach używamy wtedy, kiedy potrzebujemy zrobić coś z każdym elementem jakiejś kolekcji. Uwaga: kolekcja nie może się zmieniać podczas jej przeglądania przy pomocy tej pętli.

Ponadto z każdej pętli można wyjść w dowolnym momencie przy pomocy instrukcji break;, a także można przejść do następnej iteracji (po uprzednim sprawdzeniu warunku zakończenia pętli) przy pomocy instrukcji continue;

Każdą pętlę można zastąpić każdą inną pętlą (i ewentualnie dodatkowymi instrukcjami warunkowymi), ale po co? Używajmy właściwego rodzaju pętli we właściwej sytuacji, a dzięki temu będzie łatwiej rozpoznać tę sytuację kiedy wrócimy do tego samego kodu później i będziemy sobie próbowali przypomnieć o co w tym wszystkim chodziło.

Blok instrukcji w pętli zaczynający się od {, a kończący na } można zastąpić jedną, pojedynczą instrukcją, która kończy się średnikiem. W zasadzie to jest odwrotnie, bo jedną instrukcję zawsze można zastąpić blokiem, ale nie spierajmy się o semantykę. To, że można zastąpić, nie oznacza, że należy zastąpić. Ja nie używam bloku wtedy i tylko wtedy, gdy jestem pewny, że w danym przypadku zawsze w pętli będzie wykonywana jedna instrukcja (np. gdy coś obliczamy, albo czegoś szukamy i tą instrukcją jest if). We wszystkich pozostałych przypadkach używam bloku (nawet jeśli w bloku jest tylko jedna instrukcja i bloku de facto być nie musi), bo istnieje jakaś szansa, że w przyszłości będę chciał (ja, albo ktoś inny) w jakimkolwiek celu dopisać jeszcze jedną, albo jeszcze kilka innych instrukcji razem w tej samej pętli. Jeśli tak się zdarzy (a może się tak zdarzyć zawsze), to voilà – mamy już klamerki otwierające i zamykające blok i wystarczy dopisać samą instrukcję. Czy warto tyle zachodu dla dwóch znaczków { i }? Oczywiście że tak. Ktoś (lub coś) porównujący dwa pliki źródłowe z kodem (jeden przed zmianą, a drugi po zmianie) łatwiej (czyli szybciej) stwierdzi, że zostało coś dopisane wewnątrz pętli, a nie za pętlą albo zamiast pętli.

Pętle – przykłady

Przykład pętli while, czyli „dopóki warunek jest prawdziwy wykonuj w kółko instrukcje”:

// czyszczenie bufora klawiatury ze znaków
while (Console.KeyAvailable)
    Console.ReadKey(true); // nie pokazując ich echa w terminalu

Przykład pętli do, czyli „wykonuj instrukcje aż warunek stanie się fałszywy”:

// wczytywanie liczby aż będzie poprawna i w dozwolonym zakresie
double liczba;
bool poprawna;
do
{
    poprawna = double.TryParse(Console.ReadLine(), out liczba);
    poprawna &= liczba > 0 && liczba < 100;
    if (!poprawna)
        Console.WriteLine("Źle, spróbuj jeszcze raz...");
} while (!poprawna);

Przykład pętli for, czyli „wykonuj instrukcje tyle razy ile trzeba”:

// oblicz n-tą potęgę liczby a
int potega = 1;
for (int i = 0; i < n; i++)
    potega *= a;

Przykład pętli foreach, czyli „wykonuj instrukcje dla każdego elementu z jakieś kolekcji”:

// wypisz samogłoski z ciągu znaków s
string s = "Abrakadabra";
foreach(char c in s)
    if ("AUIEOĄĘÓ".Contains(char.ToUpper(c)))
        Console.Write(c);

Ćwiczenie 1

Dana jest pętla for (i = A; i < B; i++)
Jak może ona wyglądać po zamianie na „idącą” w dół (malejące wartości zmiennej i)?

a) for (i = B; i > A - 1; i--)
b) for (i = B; i >= A; i--)
c) for (i = B - 1; i > A - 1; i--)
d) for (i = B - 1; i >= A; i--)

Ćwiczenie 2

Dana jest pętla for (i = A - 1; i > B; i--)
Jak może ona wyglądać po zamianie na „idącą” w górę (rosnące wartości zmiennej i)?

a) for (i = B + 1; i < A; i++)
b) for (i = B; i <= A - 2; i++)
c) for (i = B + 1; i <= A - 1; i++)
d) for (i = B + 1; i < A - 1; i++)

Ćwiczenie 3

Oto zadanie do samodzielnego rozwiązania. Wypisz przy pomocy 3 pętli (do, while oraz for):
a) liczby od 1 do 100;
b) liczby od 99 do 0;
c) liczby dodatnie podzielne przez 7 i mniejsze od 204;
d) wszystkie liczby z zakresu typu byte (8 bitów bez znaku);
e) wszystkie liczby z zakresu typu sbyte (8 bitów ze znakiem).

Poniżej rozwiązania, ale nie patrz na nie. Najpierw spróbuj osobiście.

byte b;
sbyte s;
Console.WriteLine("a) do");
// za dużo pisania, ale maksymalna czytelność
b = 1;
do {
    Console.Write(b);
    b++;
} while (b <= 100);
Console.WriteLine();

// inkrementacja w złym miejscu, bo nie pozwala nic w pętli
// dopisać po linii Console.Write(b++); tam jest już nowa
// wartość b; chcemy aby wartość b między klamrami { i }
// (w tzw. ciele pętli, albo wewnątrz pętli) była stała
b = 1;
do {
    Console.Write(b++);
} while (b <= 100);
Console.WriteLine();

// zbędny znak =
b = 1;
do {
    Console.Write(b);
} while (++b <= 100);
Console.WriteLine();

// wersja zalecana
b = 1;
do {
    Console.Write(b);
} while (b++ < 100);
Console.WriteLine();

Console.WriteLine("a) while");

// pętla while ewidentnie nie nadaje się do takich zadań
// (za dużo pisania, mimo że nie ma już słowa kluczowego do)
b = 1;
while (b <= 100) { Console.Write(b); b++; } Console.WriteLine();
// chyba że chcemy coś wykonać dokładnie n razy, ale za każdym
// razem nie interesuje nas który to jest raz z kolei;
// w językach (np. C, C++) gdzie konwersja liczby na wartość
// logiczną jest domyślna (implicit) wystarczy napisać tak
// (i jest to czytelne!)
//while (n--) { // // ... //}

byte n = 100; while (n--> 0)
// że n "zmierza do zera", ładne prawda?
// (kwestia gustu...)
{
    Console.Write(100 - n); // n w ciele pętli przyjmuje
    // wartości od 99 do 0, ale nie musimy go tutaj wcale używać!!!
}
Console.WriteLine();

// ale kiedy dekrementacja jest z lewej, mamy za dużo pisania,
// niepotrzebnie dwa razy powtarzamy liczbę wykonań pętli,
// ponadto nigdzie nie widać wartości minimalnej dla n, czyli zera
// ale działa oczywiście (niekomu jednak nie życzymy analizować
// takiego kodu, więc sami też go nie piszemy w ten sposób)
n = 100;
while (--n < 100)
{
    Console.Write(100 - n);
}
Console.WriteLine();

Console.WriteLine("a) for");

// dość czytelny zapis (pomimo operatora <=)
for (b = 1; b <= 100; b++)
{
    Console.Write(b);
}
Console.WriteLine();

// ale jest częściej spotykana w wersji "od zera"
for (b = 0; b < 100; )
{
    Console.Write(++b);
}
Console.WriteLine();

// albo bez udziwniania, ale za to z dodatkowym dodawaniem...
for (b = 0; b < 100; b++)
{
    Console.Write(b + 1);
}
Console.WriteLine();

// ...którego można uniknąć w oczywisty sposób (tylko że teraz
// 101 jest trochę niesugestywne, bo pętla nie drukuje tej wartości)
// to jest najczęstsza i "najbardziej poprawna" wersja
// (oczywiście wszystkie wersje są poprawne, bo robią to co trzeba)
for (b = 1; b < 101; b++) { Console.Write(b); }
Console.WriteLine();
Console.WriteLine("b) do");

b = 99; do { Console.Write(b); } while (b-- > 0);
Console.WriteLine();

Console.WriteLine("b) while");
b = 100;
while (b-- > 0)
{
    Console.Write(b);
}
Console.WriteLine();

Console.WriteLine("b) for");

// trochę dziwne
for (b = 99; b < 100; b--) { Console.Write(b); }
Console.WriteLine();
// jeśli wydaje Ci się, że powinno być tak,
// spróbuj i zrozum dlaczego to jest źle!!!
//for (b = 99; b >= 0; b--)
//{
//    Console.Write(b);
//}
//Console.WriteLine();

Console.WriteLine("c) do");

// oczywiście można tak, to jest nieoptymalne,
// ale przynajmniej czytelne
b = 1;
do
{
    if (b % 7 == 0)
    {
        Console.Write(b);
    }
} while (++b < 204);
Console.WriteLine();

// powinno być jednak użyte dodawanie, czyli coś takiego
b = 7;
do
{
    Console.Write(b);
} while ((b += 7) < 204); // uwaga: tutaj dodatkowy nawias
// jest konieczny, bo operator < mocniej wiąże niż +=
Console.WriteLine();

Console.WriteLine("c) while");

b = 0; // zaczynamy inaczej niż w pętli do
while ((b += 7) < 204) // ale warunek ten sam i całość
// jest nawet dość przejrzysta (może warto ten sposób zapamiętać?)
{
    Console.Write(b);
}
Console.WriteLine();

// bardziej czytelnie chyba się już nie da...
b = 7;
while ((b % 7 == 0) && (b < 204))
{
    Console.Write(b);
    b += 7;
}
Console.WriteLine();

// ale zawsze da się zrobić więcej "hieroglifów"
b = 0;
while (((b += 7) % 7 == 0) && (b < 204))
{
    Console.Write(b);
}
Console.WriteLine();

Console.WriteLine("c) for");

// w pętli for robimy warunek złożony z dwóch
// jednocześnie spełnianych warunków
for (b = 7; b % 7 == 0 && b < 204; b += 7)
{
    Console.Write(b);
}
Console.WriteLine();

// albo piszemy po prostu tak (można pominąć
// pierwszy z tych warunków wiedząc, że będzie zawsze prawdziwy)
for (b = 7; b < 204; b += 7)
{
    Console.Write(b);
}
Console.WriteLine();

Console.WriteLine("d) do");

// czujemy już pewne zmęczenie zagadnieniem...
b = byte.MinValue;
do
{
    Console.Write(b);
} while (b++ < byte.MaxValue);
Console.WriteLine();

Console.WriteLine("d) while");

//...aż tu nagle odkrywamy drugie dno
// dlaczego to nie działa?

//b = byte.MinValue;
//while (b <= byte.MaxValue)
//{
//    Console.Write(b);
//    b++;
//}

// poprawiamy ten koszmar
b = byte.MinValue;
while (b <= byte.MaxValue)
{
    Console.Write(b);
    b++;
    if (b == byte.MinValue) break;
}

Console.WriteLine();
            
Console.WriteLine("d) for");

// niby oczywista oczywistość, ale nauczeni
// poprzednim przykładem spodziewamy się już problemów
//for (b = byte.MinValue; b <= byte.MaxValue; b++)
//{
//    Console.Write(b);
//}
//Console.WriteLine();

// czasem krócej będzie wyrzucić ostatnią wartość poza pętlę
for (b = byte.MinValue; b < byte.MaxValue; b++)
{
    Console.Write(b);
}
Console.WriteLine(b);

// chyba że zmienna sterująca pętli nie istnieje poza pętlą
for (byte bb = byte.MinValue; bb <= byte.MaxValue; bb++)
{
    Console.Write(bb);
    if (bb == byte.MaxValue) break;
}
Console.WriteLine();

Console.WriteLine("e) do");

// postać pętli do dla sbyte jest taka sama jak dla byte
// pętla do jest zatem odporna na znak liczby w tym sensie
// i właśnie dlatego jest to zalecany sposób na przejście
// pętlą po wszystkich wartościach danego typu całkowitego
s = sbyte.MinValue;
do
{
    Console.Write(s);
} while (s++ < sbyte.MaxValue);
Console.WriteLine();

Console.WriteLine("e) while");

// while wymaga dodatkowego if przed inkrementacją
s = sbyte.MinValue;
while (s <= sbyte.MaxValue)
{
    Console.Write(s);
    if (s == sbyte.MaxValue) break;
    s++;
}
Console.WriteLine();

// albo po inkrementacji, która przekracza zakres
// (rzucanie wyjątku overflow jest domyślnie wyłączone)
s = sbyte.MinValue;
while (s <= sbyte.MaxValue)
{
    Console.Write(s);
    s++;
    if (s == sbyte.MinValue) break;
}
Console.WriteLine();

Console.WriteLine("e) for");

// podobnie for nie działa bez dodatkowego if-a,
// ale tutaj można wybrać tylko ten przed inkrementacją
for (s = sbyte.MinValue; s <= sbyte.MaxValue; s++)
{
    Console.Write(s);
    if (s == sbyte.MaxValue) break;
    // gdyby nie ta linia pętla for jest nieskończona,
    // to realne zagrożenie gdy nie ma tego zabezpieczenia,
    // a górna granica przedziału jest podawana
    // przez użytkownika lub wczytywana z zewnętrznego pliku
}
Console.WriteLine();

// takie dodatkowe if-y to jednak nieoptymalne rozwiązanie
// Może lepiej użyć typ o większym zakresie, na przykład short
// i skorzystać z domyślnych konwersji?
// Pytanie "Co będzie szybsze?" potraktuj jako zadanie domowe :-)
for (short sh = sbyte.MinValue; sh <= sbyte.MaxValue; sh++)
{
    Console.Write(sh);
}
Console.WriteLine();

Spis treści Poprzednia strona: Instrukcja warunkowa Następna strona: Tablice

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *