Improve introductory content and clarify programming concepts in the book

This commit is contained in:
2025-09-25 02:11:45 +02:00
parent a396e0aeed
commit ad18c3ead3

168
main.typ
View File

@@ -27,21 +27,26 @@
#show: codly-init.with()
= Wstęp
Witam Ciebie bardzo serdecznie w mojej książce o programowaniu. Cieszę się, że zdecydowałeś drogi czytelniku, się na ten krok, który otworzy przed Tobą drzwi do świata technologii, w którym nie ma rzeczy niemożliwych oraz nie ma ograniczeń. Mam nadzieję, że ta książka pozwoli Ci zapuścić korzenie w tym świecie i rozwiniesz skrzydła.
Witaj w świecie programowania!
Możesz zastanawiać się czy ta książka jest dla Ciebie i to jest dobre pytanie, dla kogo jest ta książka?
Cieszę się, drogi czytelniku, że zdecydowałeś się na ten krok. Otworzy on przed Tobą drzwi do fascynującego świata technologii, w którym jedynym ograniczeniem jest wyobraźnia. Mam nadzieję, że ta książka pozwoli Ci zapuścić w tym świecie korzenie i rozwinąć skrzydła.
Moim celem jest nauczenie kogokolwiek programowania, niezależnie od wieku, płci, wykształcenia czy doświadczenia. Jeśli potrafisz czytać i pisać, to ta książka jest dla Ciebie. Nie tracimy, więc czasu, zaparzmy sobie herbatę i zaczynajmy.
Być może zastanawiasz się, czy jest to książka dla Ciebie. To świetne pytanie! Moim celem jest nauczenie kogokolwiek programowania, niezależnie od wieku, płci, wykształcenia czy doświadczenia. Jeśli potrafisz czytać i pisać, to ten podręcznik jest właśnie dla Ciebie. Nie traćmy więc czasu. Zaparzmy herbatę i zaczynajmy.
Słowem wstępu, chciałbym Cię poinformować, że zamysłem tej książki jest nauka programowania w szerokim tego słowa znaczeniu. Nie będę skupiał się na konkretnym języku programowania, ale na ogólnych zasadach, konceptach i paradygmatach, które występują w programowaniu. Dzięki temu, niezależnie od tego, jakiego języka programowania użyjesz w przyszłości, będziesz miał solidne podstawy, które pozwolą Ci szybko się go nauczyć. Natomiast przykłady kodu będą głównie w języku C\# w wersji 12, nie martw się jeśli nigdy nie miałeś z nim do czynienia, albo jeśli wersja się nie zgadza, ponieważ zagadnienia, które poruszam  uniwersalne i występują (mniej lub bardziej) w każdym języku programowania.
Chciałbym wyjaśnić Ci zamysł tej książki. Nie skupia się ona na nauce jednego, konkretnego języka, ale na uniwersalnych zasadach, koncepcjach i wzorcach myślenia, które stanowią esencję programowania. Dzięki takiemu podejściu, niezależnie od tego, jaką technologię wybierzesz w przyszłości, będziesz dysponować solidnym fundamentem, który pozwoli Ci błyskawicznie opanować każde nowe narzędzie.
Z racji tej, że skupiam się na esencji tej sztuki jaką jest programowanie, to niestety ta książka nie poinstruuje jak zainstalować środowisko programistyczne i podobne rzeczy. Zakładam, że czytelnik potrafi sobie z tym poradzić samodzielnie. Jeśli jednak nie, to w internecie jest mnóstwo poradników jak to zrobić. Proszę o wybaczenie jeśli zawiodę kogoś tym faktem.
Jako język demonstracyjny dla przykładów kodu wybrałem C\# w wersji 12. Nie martw się jednak, jeśli nigdy nie miałeś z nim do czynienia lub pracujesz na innej wersji. Zagadnienia, które poruszam, na tyle uniwersalne, że z łatwością odnajdziesz je w niemal każdym współczesnym języku programowania.
*Ważna uwaga*: Ta książka koncentruje się wyłącznie na sztuce programowania. Celowo pomijam w niej kwestie techniczne, takie jak instalacja i konfiguracja środowiska programistycznego. Zakładam, że poradzisz sobie z tym samodzielnie, korzystając z jednego z wielu doskonałych poradników dostępnych w internecie.
Każdy rozdział ma podobną strukturę: najpierw wprowadzę Cię w nowe zagadnienie, następnie zilustruję je praktycznym przykładem kodu, który szczegółowo omówię. Na końcu każdego tematu znajdziesz zestaw ćwiczeń do samodzielnego rozwiązania, które pozwolą Ci utrwalić wiedzę. Odpowiedzi do zadań umieściłem na końcu książki, ale gorąco zachęcam Cię do podejmowania własnych prób i eksperymentowania. To najlepsza droga, by nauczyć się programować.
Gotowy na przygodę? Zaczynajmy!
Struktura rozdziałów będzie mniej więcej taka sama. Najpierw wprowadzę temat, następnie pokażę przykład kodu, potem wyjaśnię jak on działa, a na końcu dam kilka ćwiczeń do samodzielnego rozwiązania. Odpowiedzi do ćwiczeń znajdziesz na końcu książki. Zachęcam do samodzielnego rozwiązywania zadań oraz do eksperymentowania z kodem. To najlepszy sposób na naukę programowania.
= Podstawy programowania
W tym rozdziale przedstawię podstawowe koncepty programowania, które fundamentem dla dalszej nauki. Omówię takie tematy jak zmienne, typy danych, instrukcje sterujące programem, funkcje czy klasy i obiekty.
== Pierwszy program
Tradycją jest, że pierwszy program w etapie edukacji programowania to program "Hello, World!", który wyświetla tytułowy tekst na ekranie. Oto jak wygląda ten program w języku C\#.
Zgodnie z tradycją, pierwszym programem pisanym podczas nauki programowania jest "Hello, World!". Jego celem jest po prostu wyświetlenie tego napisu na ekranie. Zobaczmy, jak wygląda to w języku C\#.
=== Kod
#codly(languages: codly-languages, zebra-fill: none, display-icon: false, display-name: false)
```cs
@@ -54,32 +59,52 @@ Po uruchomieniu powyższego kodu, na ekranie pojawi się tekst:
Hello, World!
```
=== Wyjaśnienie
Te dwa wiersze kodu wystarczą, aby wyświetlić tekst na ekranie. Niesamowite, prawda? Przyjrzyjmy się bliżej, jak to działa.
1. `using System;` - Ten wiersz importuje przestrzeń nazw `System`, która zawiera podstawowe klasy i funkcje, takie jak `Console`. Dzięki temu możemy wyświetlić na podstawowym urządzaniu wyjściowym (zazwyczaj jest to ekran konsoli) tekst.
2. `Console.WriteLine("Hello, World!");` - Ta linia kodu wywołuje metodę `WriteLine` klasy `Console`, która wyświetla tekst przekazany jako argument. W tym przypadku, przekazujemy tekst `"Hello, World!"`, który zostanie wyświetlony na ekranie. Co więcej, metoda `WriteLine` automatycznie dodaje znak nowej linii po wyświetleniu tekstu, więc kursor przechodzi do następnej linii.
Zaledwie dwie linijki kodu wystarczyły, aby komputer wykonał nasze polecenie. Niesamowite, prawda? Przeanalizujmy je krok po kroku.
Okej, ale to nadal brzmi jak czarna magia, prawda? Spróbuję wyjaśnić to nastepującą analogią. Ten wiersz informuje komputer, ze hej, będę uzywał standardowego zestawu narzędzi o nazwie `System`. `Console` to jedno z tych narzedzi (jak mlotek), a `WriteLine()` to konkretna czynność, którą tym narzedziem wykonujemy ("uderz w gwozdz z napisem `Hello World`").
1. `using System;` - Ta linijka to informacja dla programu: "Będziemy korzystać ze standardowego zestawu narzędzi o nazwie System". System to fundamentalna biblioteka w C\#, zawierająca podstawowe funkcjonalności. Dołączając ją, zyskujemy dostęp do wielu gotowych komponentów, w tym do obsługi konsoli.
2. `Console.WriteLine("Hello, World!");` - To jest serce naszego programu. Rozbijmy je na części:
- `Console` - to nazwa gotowego *obiektu* (możemy myśleć o nim jak o narzędziu), który reprezentuje konsolę tekstową czyli nasze okno wyjściowe.
- `.WriteLine(...)` - kropka oddziela obiekt od *metody*, czyli czynności, którą chcemy na nim wykonać. `WriteLine` to polecenie "Napisz linię".
- `("Hello, World!")` - w nawiasach podajemy *argument*, czyli dane, na których metoda ma operować. W tym przypadku jest to tekst, który ma zostać wyświetlony.
Całą linię możemy przeczytać jako: "Używając narzędzia `Console`, wykonaj polecenie `WriteLine` z tekstem `Hello, World!`".
Co więcej, metoda `WriteLine` automatycznie dodaje na końcu znak nowej linii, dzięki czemu następny tekst pojawiłby się w kolejnym wierszu.
Być może brzmi to jeszcze nieco skomplikowanie. Spróbujmy więc posłużyć się prostszą analogią. Wyobraź sobie, że `System` to wielka skrzynka z narzędziami. `Console` to jedno z tych narzędzi (nasz terminal tekstowy). Z kolei `WriteLine()` to konkretna funkcja, którą to narzędzie potrafi wykonać: "wyświetl linię tekstu". Tekst w nawiasie to po prostu treść, którą przekazujemy do wyświetlenia.
*Ważna uwaga*: Zwróć uwagę, że linia z poleceniem kończy się średnikiem (;). W C\# (i wielu innych językach) średnik działa jak kropka w zdaniu, sygnalizuje koniec instrukcji. Jego pominięcie jest jednym z najczęstszych błędów popełnianych przez początkujących programistów.
Istotna uwaga, w wielu językach programowania, w tym w C\#, każda linia kodu kończy się średnikiem (`;`). To sygnalizuje kompilatorowi, że to koniec instrukcji. Pamiętaj o tym, ponieważ pominięcie średnika spowoduje błąd podczas kompilacji.
== Czym jest program?
Kompilacja? Błąd? O co chodzi? Takie pytania pewnie pojawiły się w Twojej głowie. Spokojnie, wszystko to wyjaśnię. Na początku warto zrozumieć, czym jest program.
Kompilacja? Błąd? O co chodzi? Jeśli te pytania pojawiły się w Twojej głowie, to świetnie. aśnie teraz wszystko sobie wyjaśnimy.
Program to nic innego jak zestaw instrukcji, które komputer może wykonać. Instrukcje te napisane w języku programowania, który jest zrozumiały dla ludzi, ale musi zostać przetłumaczony na język maszynowy, który jest zrozumiały dla komputera. Ten proces tłumaczenia nazywa się kompilacją. Kiedy program jest skompilowany, powstaje plik wykonywalny, który można uruchomić na komputerze.
Najprościej mówiąc, *program komputerowy to zestaw instrukcji*, które komputer potrafi wykonać. Piszemy je w języku programowania (jak C\#), który jest dla nas zrozumiały. Komputer posługuje się jednak językiem maszynowym, składającym się głównie z zer i jedynek. Proces tłumaczenia naszego kodu na język maszynowy nazywa się *kompilacją*. W jej wyniku powstaje plik wykonywalny, gotowy do uruchomienia program.
Bardzo istotne jest, by pamiętać, ze program wykonuje się od góry do dołu.
Zaczyna się od punktu wejścia, często jest to funkcja `main()`.
Bardzo istotne jest, by pamiętać, że instrukcje w programie wykonują się w określonej kolejności, zazwyczaj *od góry do dołu*. Wykonywanie kodu rozpoczyna się w specjalnym miejscu, zwanym *punktem wejścia*. W wielu językach, w tym w C\#, jest nim tradycyjnie funkcja o nazwie `main()`.
Natomiast nie wszystkie języki programowania wymagają kompilacji. Niektóre języki, takie jak Python czy JavaScript, interpretowane, co oznacza, że instrukcje tłumaczone na język maszynowy przez specjalny program zwany interpreterem, w czasie rzeczywistym, podczas uruchamiania programu. Programy w językach interpretowanych wymagają od użytkownika posiadania interpretera zainstalowanego na komputerze, oraz często wolniejsze od programów skompilowanych.
_Możesz teraz słusznie zauważyć, że w naszym pierwszym programie "Hello, World!" nie było żadnej funkcji `main()`. To dlatego, że nowoczesny C\# dla uproszczenia pozwala ją ukryć w małych programach. Kompilator wciąż tworzy ją dla nas automatycznie "w tle". W bardziej złożonych aplikacjach zobaczymy ją już napisaną jawnie._
Warto wspomnieć, że między kompilacją, a uruchomieniem programu, występuje jeszcze etap linkowania, który polega na dołączeniu do naszego programu dodatkowych bibliotek, które dostarczają dodatkową funkcjonalność. Na przykład, w naszym pierwszym programie, linia `using System;` wskazuje, że korzystamy z biblioteki `System`, która zawiera klasę `Console` i jej metodę `WriteLine`.
Nie wszystkie języki wymagają kompilacji. Niektóre, jak Python czy JavaScript, *interpretowane*. Oznacza to, że specjalny program zwany interpreterem tłumaczy nasz kod na zyk maszynowy na bieżąco, w momencie jego uruchamiania. Programy pisane w takich językach wymagają, aby na komputerze użytkownika był zainstalowany odpowiedni interpreter.
Różnicą między językami kompilowanymi, a interpretowanymi też inne aspekty, takie jak informowanie o błędach, w językach kompilowanych błędy wykrywane podczas kompilacji co oznacza, że program nie uruchomi się dopóki wszystkie błędy nie zostaną naprawione. W językach interpretowanych błędy wykrywane podczas uruchamiania programu, co oznacza, że program może s uruchomić, ale może napotkać błąd w trakcie działania i wtedy się zatrzymać. Czy to oznacza, że w programach w językach kompilowanych jeśli podczas etapu kompilacji nie wystąpią błędy to program będzie działał bezbłędnie? Niestety nie, ponieważ mogą wystąpić błędy logiczne, bądź błędy wykonania, które nie wykrywane podczas kompilacji.
Natomiast kompilator stoi na straży poprawności składniowej kodu, co jest bardzo przydatne, bo pozwala programiście na uniknięciu wielu błędów już na etapie pisania kodu.
Warto też wspomnieć, że między kompilacją a uruchomieniem programu zachodzi jeszcze *linkowanie*. To etap, na którym do naszego kodu dołączane są dodatkowe, gotowe biblioteki. W naszym przykładzie `using System;` dało kompilatorowi znać, że będziemy korzystać z biblioteki `System`, a linker zadbał o to, by jej funkcjonalność została poprawnie "połączona" z naszym programem.
Jakie to ma dla nas znaczenie w praktyce? Jedną z kluczowych różnic jest sposób wykrywania błędów. W językach kompilowanych (jak C\#) kompilator analizuje cały kod, zanim jeszcze program zostanie uruchomiony. Jeśli znajdzie błędy składniowe (np. literówkę w nazwie polecenia lub brak średnika), przerwie kompilację i nie pozwoli uruchomić programu, dopóki ich nie naprawimy.
W językach interpretowanych błędy często wykrywane dopiero w momencie, gdy interpreter natrafi na problematyczną linię podczas działania programu. Może to oznaczać, że program uruchomi się i będzie działał przez jakiś czas, a następnie nagle przerwie swoje działanie z powodu błędu.
Czy to oznacza, że program skompilowany bez błędów zawsze będzie działał poprawnie? Niestety nie. Kompilator jest strażnikiem składni, ale nie rozumie logiki naszego programu. Może on wykryć błąd składniowy, ale nie *błąd logiczny* (np. gdy każemy programowi dodać dwie liczby, a tak naprawdę chcieliśmy je pomnożyć) ani *błąd wykonania* (np. próba dzielenia przez zero). Tego typu problemy wciąż mogą pojawić się podczas działania aplikacji.
== Typy danych
W następnym rozdziale wprowadzę koncept "pudełek" na dane, czyli zmiennych. W prawdziwym świecie, mamy różne rodzaje pudełek na różne przedmioty, nie wsadzimy przecież telewizora do pudełka po butach, bo się po prostu nie zmieści. Podobnie jest w naszych cyfrowych "pudełkach". Różne rodzaje danych wymagają różnych typów. To właśnie typ określa, jaką wartość można przechować w zmiennej. Decyduje on również o tym, ile miejsca ta wartość zajmie w pamięci komputera. Wybór odpowiedniego typu danych jest kluczowy, ponieważ wpływa na wydajność programu oraz na to, jakie operacje można wykonywać na tych danych. O tym ile miejsca w pamięci zajmuje dany typ danych, decyduje architektura systemu operacyjnego na którym działa program (np. 32-bitowy lub 64-bitowy) oraz implementacja języka programowania.
Wyobraź sobie, że dane w programie przechowujemy w pudełkach. W prawdziwym świecie mamy różne rodzaje pudełek na różne przedmioty telewizora nie włożymy do pudełka po butach, bo po prostu się nie zmieści.
Poniżej omówię kilka podstawowych typów danych, które powszechnie ywane w programowaniu wraz z ich rozmiarem w pamięci na typowej 64-bitowej architekturze i przykłady ich zastosowania.
Różne rodzaje danych wymagają różnych "cyfrowych pudełek". W naszym świecie nazywamy je *typami danych.* To właśnie typ określa, jaką wartość można przechować w danym miejscu w pamięci. Decyduje on również o tym, ile miejsca ta wartość zajmie i jakie operacje będzie można na niej wykonywać. Wybór odpowiedniego typu jest kluczowy dla wydajności i poprawności programu.
Na początku naszej przygody z programowaniem będziemy korzystać głównie z kilku podstawowych typów:
- `int` - do przechowywania liczb całkowitych (np. `10`, `-5`, `2024`).
- `double` - do przechowywania liczb ułamkowych (np. `3.14`, `-0.5`).
- `string` - do przechowywania tekstu (np. `"Ania"`, `"Hello, World!"`).
- `bool` - do przechowywania wartości logicznych (prawda/fałsz, czyli `true`/`false`).
Oczywiście, języki programowania oferują znacznie więcej wyspecjalizowanych typów. Poniższa tabela przedstawia podstawowe typy danych dostępne w języku C\#. Traktuj jako podręczną ściągawkę, do której zawsze możesz wrócić.
#table(
columns: (auto, auto, 1fr, auto),
@@ -104,11 +129,13 @@ Poniżej omówię kilka podstawowych typów danych, które są powszechnie używ
W tabeli powyżej przedstawiłem podstawowe typy danych w języku C\#. Oczywiście, istnieją też bardziej zaawansowane typy danych, takie jak tablice, listy, czy klasy, które omówię w dalszej części książki.
Warto zauważyć, że mamy do typów liczbowych podzi na liczby ze znakiem i bez znaku. Liczby ze znakiem (np. `int`, `long`) mogą przechowywać zarówno wartości dodatnie, jak i ujemne, natomiast liczby bez znaku (np. `uint`, `ulong`) mogą przechowywać tylko wartości dodatnie, ale za to mają większy zakres wartości dodatnich, ponieważ nie muszą rezerwować miejsca na liczby ujemne.
Warto zauważyć, że typy liczbowe dzielą się na liczby *ze znakiem* i *bez znaku*. Liczby ze znakiem (np. `int`, `long`) mogą przechowywać wartości dodatnie i ujemne. Liczby bez znaku (np. `uint`, `ulong`) przechowują tylko wartości dodatnie, ale dzięki temu mają dwukrotnie większy zakres dodatni.
Typ `float` jest typem zmiennoprzecinkowym pojedynczej precyzji, co oznacza, że przechowuje liczby z mniejszą dokładnością (około 7 cyfr znaczących). Natomiast `double` jest typem zmiennoprzecinkowym podwójnej precyzji, który przechowuje liczby z większą dokładnością (około 15-16 cyfr znaczących). Typ `decimal` jest używany głównie do obliczeń finansowych, ponieważ oferuje bardzo wysoką precyzję i unika problemów związanych z reprezentacją liczb zmiennoprzecinkowych.
Typ `float` to typ zmiennoprzecinkowy pojedynczej precyzji (dokładność do ok. 7 cyfr), a `double` podwójnej precyzji (dokładność do ok. 15-16 cyfr). Do obliczeń finansowych, gdzie precyzja jest absolutnie krytyczna, używa się typu `decimal`, który eliminuje typowe dla `float` i `double` błędy zaokrągleń.
Możesz się zastanawiać, dlaczego typ `bool` zajmuje 8 bitów, skoro może przyjmować tylko dwie wartości: `true` lub `false`. Powodem jest to, że w większości architektur komputerowych, najmniejszą jednostką pamięci, którą można zaadresować, jest bajt (8 bitów). Dlatego nawet jeśli zmienna typu `bool` potrzebuje tylko jednego bitu do przechowywania swojej wartości, to i tak zajmuje cały bajt w pamięci. Aczkolwiek istnieją sposoby, by zaoszczędzić miejsce, na przykład poprzez pakowanie wielu wartości `bool` w jeden bajt, ale to już bardziej zaawansowany temat.
_Ciekawostka: Zwróć uwagę na litery `f` i `m` w przykładach `3.14f` i `79.99m`. W C\# informują one kompilator, że dana liczba ma być traktowana jako `float` lub `decimal`. Domyślnie każda liczba z kropką dziesiętną jest traktowana jako `double`._
Być może zastanawiasz się, dlaczego typ `bool` zajmuje 8 bitów, skoro potrzebuje tylko jednego do przechowania wartości `true` lub `false`. Dzieje się tak, ponieważ najmniejszą jednostką pamięci, do której komputer może się bezpośrednio odwołać, jest bajt (8 bitów). Nawet jeśli realnie potrzebujemy tylko jednego bitu, musimy zarezerwować cały bajt.
== Zmienne
No dobrze, nasz pierwszy program był bardzo prosty i przez to można by powiedzieć, że nie był zbyt ciekawy, bo przecież programy, do których jesteśmy przyzwyczajeni robią coś więcej niż tylko wyświetlanie tekstu na ekranie.
@@ -129,22 +156,26 @@ Console.WriteLine("Next year, you will be " + nextYearAge + " years old.");
```
=== Wyjaśnienie
Przyjrzyjmy się, jak działa ten program krok po kroku.
1. `int age;` - Ta linia deklaruje zmienną o nazwie `age`, która jest typu `int` (liczba całkowita). Deklaracja zmiennej mówi kompilatorowi, że chcemy zarezerwować miejsce w pamięci na przechowywanie wartości tego typu.
2. `Console.WriteLine("Enter your age:");` - Ta linia wyświetla na ekranie prośbę o podanie wieku.
3. `age = Convert.ToInt32(Console.ReadLine());` - W tej linijce dzieje się sporo, ale spokojnie, zacznijmy od środka. `Console.ReadLine()` czeka az uzytkownik wpisze cokolwiek i nacisnie klawisz `Enter`. *Wazne*: cokolwiek zostanie wpisane, nasz program potraktuje to jako `string` (tekst). Nastepnie ten tekst wpisany przez uzytkownika trafia do metody `Convert.ToInt32()`, ta funkcja probuje "przerobic" nasz tekst na liczbe calkowita (int).
4. `Console.WriteLine("You are " + age + " years old.");` - Ta linia wyświetla na ekranie wiek użytkownika, korzystając z wartości przechowywanej w zmiennej `age`.
5. `int nextYearAge = age + 1;` - Ta linia deklaruje nową zmienną `nextYearAge`, która przechowuje wartość wieku użytkownika powiększoną o 1, co reprezentuje jego wiek w następnym roku.
6. `Console.WriteLine("Next year, you will be " + nextYearAge + " years old.");` - Ta linia wyświetla na ekranie wiek użytkownika w następnym roku, korzystając z wartości przechowywanej w zmiennej `nextYearAge`.
1. `int age;` - Ta linia *deklaruje* zmienną o nazwie `age`, która będzie przechowywać dane typu `int` (liczba całkowita). Deklaracja to jakby zarezerwowanie w pamięci komputera pustego pudełka z etykietą "age".
2. `Console.WriteLine("Enter your age:");` - Ta linia, tak jak w poprzednim programie, wyświetla na ekranie prośbę o podanie wieku.
3. `age = Convert.ToInt32(Console.ReadLine());` - Tutaj dzieje się najwięcej, więc przeanalizujmy to od środka:
- `Console.ReadLine()` - ta część czeka, aż użytkownik coś wpisze i naciśnie klawisz `Enter`. Co ważne, niezależnie od tego, co wpiszesz, program zawsze odczyta to jako tekst (`string`).
- `Convert.ToInt32(...)` - odczytany tekst jest następnie przekazywany do tej metody. Jej zadaniem jest próba zamiany (konwersji) tekstu na liczbę całkowitą (`int`). Ten proces nazywamy *konwersją typów*.
- `age = ...` - na końcu, wynik konwersji (już jako liczba) jest przypisywany do naszej zmiennej `age`.
_Co by się stało, gdyby użytkownik wpisał "abc" zamiast liczby? Program zakończyłby działanie błędem. Na razie zakładamy, że dane wejściowe są poprawne. Obsługą nieoczekiwanych sytuacji zajmiemy się w dalszych rozdziałach._
4. `Console.WriteLine("You are " + age + " years old.");` - Ta linia wyświetla wynik. Znak `+` użyty w ten sposób łączy ze sobą fragmenty tekstu oraz wartość zmiennej `age` w jeden ciąg znaków. Nazywamy to *konkatenacją*.
5. `int nextYearAge = age + 1;` - Deklarujemy nową zmienną `nextYearAge` i od razu przypisujemy jej wartość. wartością jest wynik prostego działania matematycznego: zawartość zmiennej `age` plus `1`.
6. `Console.WriteLine("Next year, you will be " + nextYearAge + " years old.");` - Na koniec, podobnie jak wcześniej, wyświetlamy wiek użytkownika w przyszłym roku, korzystając z nowej zmiennej.
=== Ćwiczenia
1. Napisz program, który pyta użytkownika o jego imię i wyświetla powitanie z użyciem tego imienia.
2. Napisz program, który pyta użytkownika o dwie liczby całkowite i wyświetla ich sumę.
3. Napisz program, który pyta użytkownika o promień koła i oblicza oraz wyświetla jego pole powierzchni (użyj wzoru: pole = $pi * r^2, "gdzie" pi approx 3.14$).
1. Napisz program, który pyta użytkownika o jego imię i wyświetla spersonalizowane powitanie (np. "Witaj, Aniu!").
2. Napisz program, który prosi użytkownika o podanie dwóch liczb całkowitych, a następnie wyświetla ich sumę.
3. Napisz program, który pyta użytkownika o promień koła (może to być liczba z częścią ułamkową) i na tej podstawie oblicza oraz wyświetla pole jego powierzchni (użyj wzoru: pole = $pi * r^2, "gdzie" pi approx 3.14$).
== Instrukcje sterujące programem
W tym rozdziale omówię w jaki sposób jesteśmy w stanie sterować przepływem wykonania naszego programu. W programowaniu mamy dostępne kilka rodzai instrukcji sterujących. to instrukcje warunkowe, czyli wykonaj kod jeśli dany warunek jest spełniony. Instrukcje powtarzające się (pętle), czyli wykonaj dany fragment X razy. Instrukcje skoku, czyli bezpośrednio przejdź do danego miejsca. Z tych ostatnich generalnie nie powinno się korzystać, bo wprowadzają chaos i trudność analizy kodu później. No i na samym końcu mamy hybrydowe zestawienie, które łaczy skok z warunkami (switch, match).
W tym rozdziale omówię, w jaki sposób jesteśmy w stanie sterować przepływem wykonania naszego programu. W programowaniu mamy dostępne kilka rodzajów instrukcji sterujących. to instrukcje warunkowe, czyli wykonaj kod jeśli dany warunek jest spełniony. Instrukcje powtarzające się (pętle), czyli wykonaj dany fragment X razy. Instrukcje skoku, czyli bezpośrednio przejdź do danego miejsca. Z tych ostatnich generalnie nie powinno się korzystać, bo wprowadzają chaos i trudność analizy kodu później. No i na samym końcu mamy hybrydowe zestawienie, które łączy skok z warunkami (switch, match).
=== Instrukcje warunkowe
Jak wspomniałem instrukcje warunkowe pozwalają wykonać kod, gdy pewny warunek jest spełniony. Jest to bardzo uzyteczne narzędzie, bez którego programy nie byłyby za ciekawe.
Jak wspomniałem instrukcje warunkowe pozwalają wykonać kod, gdy pewien warunek jest spełniony. Jest to bardzo użyteczne narzędzie, bez którego programy nie byłyby za ciekawe.
==== Przykład
```cs
int age = 20;
@@ -185,31 +216,80 @@ Gratuluję, jesteś pełnoletni!
Mozesz byc posłem.
```
==== Wyjaśnienie
Jak widać na przykładzie instrukcja warunkowa składa się z konstrukcji
Jak widać na przykładzie, podstawowa instrukcja warunkowa ma konstrukcję:
```cs
if (warunek)
{
// kod
// Kod do wykonania, jeśli warunek jest prawdziwy
}
```
co oznacza, jeśli (if) warunek będzie prawdziwy to wykonaj kod, który jest między klamrami.
Możemy przeczytać jako: "jeśli (`if`) warunek w nawiasach jest prawdziwy, to wykonaj kod zawarty między klamrami".
Natomiast, co jeśli mamy taka sytuację chcemy, by pewien kod wykonał się jeśli warunek jest spełniony, a jeśli nie (else) to inny fragment powinien się wykonać. Do tego właśnie słuzy konstrukcja
Często chcemy jednak, aby w przypadku niespełnienia warunku wykonał się inny fragment kodu. Do tego służy konstrukcja `else` (w przeciwnym razie):
```cs
if (warunek)
{
// waurnek spelniony, wykonaj ten fragment
// Warunek spełniony, wykonaj ten fragment
}
else
{
// w przeciwnym razie wykonaj to
// W przeciwnym razie, wykonaj to
}
```
W przykładzie przedstawiłem jeszcze jedna konstrukcje `if else`, która oznacza, jeśli warunek 1 nie został spełniony to sprawdź i wykonaj warunek 2. Mozemy jeszcze tak jak w przykłdzie dodać `else` na końcu przez co dostaniemy taka konstrukcję: sprawdź warunek 1, jeśli nie jest spełniony sprawdź warunek 2, jeśli warunek 1 i warunek 2 niespełnione to wykonaj kod od `else`.
W naszym przykładzie użyliśmy jeszcze bardziej rozbudowanej formy: łańcucha `else if`. Pozwala on na sprawdzanie wielu warunków po kolei:
```cs
if (warunek1) { ... }
else if (warunek2) { ... }
else if (warunek3) { ... }
else { ... }
```
Wazne, mozemy mieć wiele `if else` i one sprawdzane od góry do dołu.
Program sprawdza warunki od góry do dołu. Gdy tylko natrafi na *pierwszy prawdziwy warunek*, wykonuje przypisany do niego blok kodu i *ignoruje wszystkie pozostałe* części tej konstrukcji (`else if` oraz `else`). Jeśli żaden z warunków nie jest prawdziwy, wykonywany jest opcjonalny blok `else` na samym końcu.
*Kolejność ma kluczowe znaczenie!*
Zwróć uwagę, jak ważna jest kolejność warunków w drugim przykładzie. Gdybyśmy odwrócili logikę i zaczęli od najniższego progu:
```cs
// NIEPOPRAWNA KOLEJNOŚĆ - BŁĄD LOGICZNY!
if (age >= 18) { Console.WriteLine("Możesz głosować."); }
else if (age >= 21) { Console.WriteLine("Możesz być posłem."); }
else if (age >= 35) { Console.WriteLine("Możesz być prezydentem."); }
```
Dla osoby w wieku 40 lat program sprawdziłby pierwszy warunek (`40 >= 18`), uznał go za prawdziwy i wyświetlił "Możesz głosować.", po czym zakończyłby sprawdzanie. Nigdy nie dowiedzielibyśmy się, że ta osoba ma również inne prawa. Dlatego warunki w takich łańcuchach zawsze układamy *od najbardziej szczegółowego do najbardziej ogólnego*.
W nawiasach przy `if` umieszczamy wyrażenie, które musi dać w wyniku prawdę lub fałsz. Wyrażenie `age >= 18` wykorzystuje operator porównania `>=`. W programowaniu mamy do dyspozycji cały zestaw takich operatorów, bardzo podobnych do tych znanych z matematyki.
#table(
columns: (auto, auto),
inset: 10pt,
align: horizon,
table.header([Operator], [Znaczenie]),
"==", "jest równe",
"!=", "nie jest równe",
">", "większe niz",
">=", "większę bądź równe",
"<", "mniejsze niż",
"<=", "mniejsze bądź równe",
"&&", "i",
"||", "lub",
"!", "nie (negacja)",
)
_*Ważna uwaga*: Pamiętaj, aby do *porównywania* wartości zawsze używać podwójnego znaku równości `==`. Pojedynczy znak `=` służy do przypisywania wartości do zmiennych. Pomylenie tych dwóch operatorów to jeden z najczęstszych błędów początkujących._
Wynikiem działania operatora porównania jest zawsze wartość typu `bool` (`true` lub `false`). Możemy to wykorzystać, aby pisać bardziej czytelny kod:
```cs
bool isAdult = age >= 18;
if (isAdult)
{
Console.WriteLine("Gratuluję, jesteś pełnoletni!");
}
```
W tym fragmencie najpierw obliczamy warunek i zapisujemy jego wynik ( `true` lub `false`) w zmiennej `isAdult`. Następnie sama zmienna staje się warunkiem w instrukcji `if`. Zauważ, że nie musimy pisać` if (isAdult == true)`, ponieważ zmienna `isAdult` już sama w sobie przechowuje dokładnie taką wartość, jakiej oczekuje instrukcja `if`. Aby sprawdzić, czy jest fałszywa, używamy operatora negacji: `if (!isAdult)`.
==== Ćwiczenia
1. Napisz program, który pyta użytkownika o jego wiek i wyświetla odpowiednią wiadomość w zależności od tego, czy jest on pełnoletni (18 lat lub więcej), nastolatkiem (13-17 lat) czy dzieckiem (12 lat lub mniej).
2. Napisz program, który prosi użytkownika o podanie liczby całkowitej i sprawdza, czy jest ona parzysta czy nieparzysta.
3. Napisz program, który pyta użytkownika o jego ocenę (0-100) i wyświetla odpowiednią wiadomość: "Niedostateczna" (0-59), "Dostateczna" (60-69), "Dobra" (70-79), "Bardzo dobra" (80-89), "Celująca" (90-100). Jeśli ocena jest poza tym zakresem, wyświetl "Nieprawidłowa ocena".
=== Pętle
==== Przykład
==== Wyjaśnienie