This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
dydaktyka:cprog:2016:loops [2016/10/27 12:29] pkleczek [Zadanie SQUARE] |
— (current) | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ~~NOTRANS~~ | ||
- | ====== Pętla for, pętla "while", debuggowanie ====== | ||
- | |||
- | ===== Cel laboratorium ===== | ||
- | |||
- | * Omówienie priorytetów operatorów. | ||
- | * Zapoznanie z pętlą ''for'', ''while'' i ''do-while''. | ||
- | * Umiejętność posługiwania się //debuggerem//. | ||
- | |||
- | ===== Pętla "for" ===== | ||
- | |||
- | Pętla ''for'' (ang. //dla//) pozwala na wykonywanie fragmentu programu określoną ilość razy. | ||
- | |||
- | Wykorzystuje ona trzy wyrażenia rozdzielone __średnikami__: | ||
- | * **inicjalizacja licznika** -- wykonywane jeden raz, zaraz przed pierwszą iteracją (obiegiem pętli) | ||
- | * **warunek** (test) -- obliczany __przed__ każdym potencjalnym wykonaniem pętli (jeśli wyrażenie to jest fałszywe, pętla zostaje zakończona) | ||
- | * **zmiana** (aktualizacja) -- obliczane __na końcu__ każdej iteracji | ||
- | |||
- | Pętla ''for'' kończy się jedną instrukcją prostą lub złożoną. | ||
- | |||
- | Pętla ''for'' to "pętla z warunkiem wejścia", co oznacza że pętla może nie wykonać się ani razu, jeśli na samym początku warunek nie został spełniony. | ||
- | |||
- | |||
- | Ogólna postać pętli ''for'': | ||
- | <code> | ||
- | for (inicjalizacja; test; aktualizacja) | ||
- | instrukcja | ||
- | </code> | ||
- | |||
- | **Schemat blokowy a kod w C** | ||
- | ^ Schemat blokowy ^ Kod w C ^ | ||
- | | {{:dydaktyka:aisd:2016:iteration.png?direct|}} | <code c> | ||
- | int licz; | ||
- | for (licz = WP; licz <= WK; licz = licz + 1) { | ||
- | instrukcja | ||
- | } | ||
- | </code> Zwróć uwagę, że w języku C pętla for wykonuje się dopóki warunek **jest** spełniony (na sąsiednim schemacie blokowym mamy iterację dopóki warunek nie był spełniony). | | ||
- | |||
- | ---- | ||
- | |||
- | **Przykład 1 -- Wielokrotne wypisywanie na ekran** | ||
- | <code c> | ||
- | #include <stdio.h> | ||
- | |||
- | int main() | ||
- | { | ||
- | int i; | ||
- | |||
- | for(i = 1; i <= 10; i = i + 1) { | ||
- | printf("%d\n", i); | ||
- | } | ||
- | |||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | |||
- | **Przykład 2 -- Obliczanie silnii** | ||
- | <code c silnia.c> | ||
- | #include <stdio.h> | ||
- | |||
- | int main() | ||
- | { | ||
- | int s; // silnia - po wykonaniu petli przechowuje wartosc n! | ||
- | int n; // liczba, dla ktorej obliczamy silnie | ||
- | int i; // zwyczajowe oznaczenie licznika petli `for` | ||
- | |||
- | printf("Podaj liczbę naturalną n: "); | ||
- | scanf("%d", &n); | ||
- | |||
- | s = 1; | ||
- | for(i = 2; i <= n; i = i + 1) { | ||
- | s = s * i; | ||
- | } | ||
- | |||
- | printf("%d! = %d\n", n, s); | ||
- | |||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | |||
- | ---- | ||
- | |||
- | **Zadanie FOR-1** \\ | ||
- | Napisz program, który oblicza sumę liczb całkowitych z zakresu od 1 do 10 przy pomocy pętli ''for''. \\ | ||
- | Oczekiwany wynik: ''55'' | ||
- | |||
- | **Zadanie FOR-2** \\ | ||
- | Napisz program, który wczytuje zadane przez użytkownika dwie liczby całkowite -- $a$ i $b$ -- i oblicza sumę liczb całkowitych z przedziału $\langle a, b \rangle$. Przyjmij, że $a \leq b$ (nie sprawdzaj tego w programie). \\ | ||
- | Przykład: Dla liczb $a = 3, b = 5$ poprawnym wynikiem jest $3+4+5=12$. | ||
- | |||
- | ===== Priorytet operatorów ===== | ||
- | |||
- | Priorytet operatorów oznacza to samo, co kolejność działań w matematyce -- operator o wyższym priorytecie zostanie wykonany najpierw, a gdy obok siebie stoją, to operacje są wykonywane od lewej do prawej (chyba, że sam operator działa inaczej, o czym za chwilę). | ||
- | |||
- | Przykładowo -- w matematyce -- wyrażenie $(2+2 \cdot 2) \cdot 2$ zostanie obliczone w następujących krokach: | ||
- | - Obliczenie wartości w nawiasie: | ||
- | - Obliczenie $2 \cdot 2 = 4$ | ||
- | - Obliczenie $2 + 4 = 6$ | ||
- | - Obliczenie $6 \cdot 2 = 12$ | ||
- | |||
- | Dlaczego tak? Ponieważ -- mówiąc językiem programisty -- nawiasy mają najwyższy priorytet, a mnożenie ma wyższy priorytet od dodawania. | ||
- | |||
- | Podobnie jak w matematyce, w języku C możesz w wyrażeniach używać nawiasów, aby obliczenia były wykonywane w odpowiedniej kolejności. | ||
- | |||
- | Zapoznaj się z [[http://en.cppreference.com/w/c/language/operator_precedence|tabelą priorytetów operatorów]] w języku C (**uwaga:** wielu z zawartych w niej operatorów jeszcze nie znasz, lecz część z nich wprowadzimy sukcesywnie na kolejnych zajęciach). \\ | ||
- | Zwróć uwagę na kolumnę //Associativity//, czyli "kierunek wiązania". Oznacza to, która strona operatora zostanie obliczona w pierwszej kolejności (np. //Left-to-right// oznacza, że najpierw obliczona zostanie lewa strona operatora) i niejako "w którą stronę działa operator" -- przykładowo operator przypisania ''='' przypisuje wartość stojącą po jego **prawej** stronie zmiennej stojącej po **lewej** stronie, zatem jego kierunek wiązania to **Right-to-Left**. | ||
- | |||
- | Tak więc, można stwierdzić, że //każde wyrażenie w języku C ma pewną wartość, obliczoną zgodnie z priorytetami i kierunkami wiązania operatorów//. | ||
- | |||
- | :!: Nie musisz pamiętać całej tej terminologii, lecz **musisz kojarzyć** o co w tym wszystkim chodzi. | ||
- | |||
- | ---- | ||
- | |||
- | **Zadanie PRIOR-1** \\ | ||
- | Jaką wartość przyjmuje w języku C wyrażenie ''3 < x < 6'' dla $x = 2$? Dlaczego? | ||
- | |||
- | ===== Pętla "while" ===== | ||
- | |||
- | Pętla ''while'' wykonuje instrukcję dopóty, dopóki spełniony jest warunek testowy (tzn. wyrażenie podane jako warunek ma wartość niezerową). | ||
- | |||
- | Ogólna postać: | ||
- | <code> | ||
- | while (wyrażenie_testowe) | ||
- | instrukcja | ||
- | </code> | ||
- | |||
- | Pętla ''while'' to //pętla z warunkiem wejścia//, co oznacza że warunek sprawdzany jest **przed** każdym jej obiegiem (także pierwszym). | ||
- | |||
- | Zazwyczaj zechcesz gdzieś wewnątrz pętli zmieniać wartości zmiennych tak, aby warunek wejścia stał się fałszywy :) | ||
- | |||
- | ---- | ||
- | |||
- | **Przykład** \\ | ||
- | Obliczanie silni za pomocą pętli ''while'' -- porównaj z [[http://home.agh.edu.pl/~pkleczek/dokuwiki/doku.php?id=dydaktyka:cprog:2015:conditionals#petla_for|programem]] wykonującym to samo zadanie z użyciem pętli ''for''. | ||
- | <code c> | ||
- | #include <stdio.h> | ||
- | |||
- | int main() | ||
- | { | ||
- | int s, n, i; | ||
- | |||
- | printf("Podaj liczbe naturalna n: "); | ||
- | scanf("%d", &n); | ||
- | |||
- | s = 1; | ||
- | i = 1; | ||
- | |||
- | while (i <= n) { | ||
- | s = s * i; | ||
- | i = i + 1; | ||
- | } | ||
- | |||
- | printf("%d! = %d\n", n, s); | ||
- | |||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | |||
- | Która pętla Twoim zdaniem lepiej nadaje się do tego zadania? | ||
- | |||
- | Dobra praktyka programistyczna mówi: \\ | ||
- | //Stosuj pętlę ''for'' tylko wtedy, gdy dokonujesz zarówno inicjalizacji, jak i aktualizacji. We wszystkich innych przypadkach lepiej (czytelniej) użyć pętli ''while''.// | ||
- | |||
- | ---- | ||
- | |||
- | **Zadanie WHILE-1** \\ | ||
- | Napisz program obliczający wynik $n \cdot k$ dla zadanych liczb całkowitych $n$ i $k$ bez korzystania z mnożenia. Użyj pętli ''while''. \\ | ||
- | //Wskazówka: $n \cdot k = \underbrace{n + n + \ldots + n}_{k \text{ razy}}$// | ||
- | |||
- | ===== Pętla "do-while" ===== | ||
- | |||
- | Pętla ''do-while'', podobnie jak pętla ''while'' wykonuje instrukcję dopóki spełniony jest warunek testowy. | ||
- | |||
- | Ogólna postać: | ||
- | <code> | ||
- | do | ||
- | instrukcja | ||
- | while (wyrażenie_testowe); | ||
- | </code> | ||
- | :!: Zwróć uwagę na średnik występujący po ''%%while (...)%%''! | ||
- | |||
- | Różnica między dwoma typami pętli ''while'' polega na tym, że ''do-while'' to //pętla z warunkiem wyjścia// -- oznacza to, że warunek sprawdzany jest **po** każdym obiegu pętli (a więc petla wykona się **co najmniej raz**). | ||
- | |||
- | ---- | ||
- | |||
- | **Przykład** | ||
- | <code c> | ||
- | #include <stdio.h> | ||
- | |||
- | int main(void) | ||
- | { | ||
- | int i = 0; | ||
- | |||
- | printf("Poczatek petli \n\n"); | ||
- | |||
- | do { | ||
- | printf("Wartosc zmiennej 'i' wynosi %d \n", i); | ||
- | i = i + 1; | ||
- | } while (i < 5); | ||
- | |||
- | printf("\nKoniec petli"); | ||
- | |||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | |||
- | ---- | ||
- | |||
- | W praktyce programiści starają się unikać stosowania ''do-while'', gdyż: | ||
- | * umieszczanie warunku na końcu pętli zmniejsza czytelność kodu, natomiast | ||
- | * bez trudu można tak zmienić kod, aby zastąpić pętlę ''do-while'' pętlą ''while''. | ||
- | |||
- | Uogólnienie powyższej zasady brzmi: \\ | ||
- | //Lepiej stosować pętle z warunkiem wejścia (''while'', ''for''), a nie wyjścia (''do-while'').// (nbsp) [size=10](z tych samych powodów, co podane powyżej)[/size] | ||
- | |||
- | ---- | ||
- | |||
- | **Zadanie DOWHILE-1** \\ | ||
- | Napisz program, który prosi użytkownika o podanie liczby całkowitej większej od $0$. Program powinien zakończyć działanie dopiero wtedy, gdy użytkownik podał poprawną wartość. | ||
- | |||
- | **Zadanie DOWHILE-2** \\ | ||
- | Napisz program losujący liczbę z przedziału $\langle 0, 100 \rangle$ tak długo aż wylosuje liczbę z przedziału $\langle 10, 15 \rangle$. \\ | ||
- | Do losowania liczb służy funkcja [[http://www.cplusplus.com/reference/cstdlib/rand/?kw=rand|rand()]] z biblioteki //stdlib.h//. | ||
- | |||
- | ===== Debugging ===== | ||
- | |||
- | Zapoznaj się z materiałem dostępnym tu: [[dydaktyka:cprog:learning_resources:debugging|Debugging]] | ||
- | |||
- | ===== Zadania podsumowujące ===== | ||
- | |||
- | ==== Zadanie STAIRS ==== | ||
- | |||
- | ([size=10]poziom trudności:[/size] {{stars>1/4}}) | ||
- | |||
- | Napisz program, który dla zadanej liczby naturalnej $n$ wyświetla "schody" o wysokości $n$. | ||
- | |||
- | Przykład dla $n = 3$: | ||
- | <code> | ||
- | $ | ||
- | $$ | ||
- | $$$ | ||
- | </code> | ||
- | |||
- | //Wskazówka: Użyj zagnieżdżonej pętli ''for''.// | ||
- | |||
- | ==== Zadanie BOX ==== | ||
- | |||
- | ([size=10]poziom trudności:[/size] {{stars>2/4}}) | ||
- | |||
- | Napisz program, który dla zadanej liczby naturalnej $n$ wyświetla "pudełko" o krawędzi długości $n$. | ||
- | |||
- | Przykład dla $n = 6$: | ||
- | <code> | ||
- | ******* | ||
- | ** * | ||
- | * * * | ||
- | * * * | ||
- | * * * | ||
- | * ** | ||
- | ******* | ||
- | </code> | ||
- | |||
- | ==== Zadanie DIGSUM ==== | ||
- | |||
- | ([size=10]poziom trudności:[/size] {{stars>2/4}}) | ||
- | |||
- | Napisz program, który dla zadanej liczby całkowitej obliczy sumę jej cyfr. \\ | ||
- | Przykład: dla $196$ poprawnym wynikiem jest $1 + 9 + 6 = 16$ | ||
- | |||
- | //Wskazówka: Wykorzystaj własność [[dydaktyka:cprog:2015:data_types#arytmetyka_liczb_w_c_-_pulapki|dzielenia z obcięciem w języku C]].// | ||
- | |||
- | ==== Zadanie PRIME ==== | ||
- | |||
- | ([size=10]poziom trudności:[/size] {{stars>3/4}}) | ||
- | |||
- | Napisz program sprawdzający w [[http://wcipeg.com/wiki/Naive_algorithm|naiwny sposób]] (czyli: najprostszy), czy zadana przez użytkownika liczba jest liczbą pierwszą. \\ | ||
- | Wykorzystaj pętlę ''for'' i operator //modulo// (''%''). | ||
- | |||
- | //Wskazówka 1: Najpierw zastanów się, jakie warunki musi spełniać liczba, aby była pierwsza (inaczej: jak "ręcznie" sprawdzić, czy liczba jest pierwsza).// \\ | ||
- | //Wskazówka 2: Przy konstruowaniu warunku pętli bądź instrukcji warunkowych pomocne mogą Ci być [[https://pl.wikipedia.org/wiki/Prawa_De_Morgana|prawa De Morgana]].// \\ | ||
- | |||
- | ==== Zadanie BABSQRT ==== | ||
- | |||
- | ([size=10]poziom trudności:[/size] {{stars>3/4}}) | ||
- | |||
- | Napisz program obliczający pierwiastek kwadratowy zadanej liczby [[https://pl.wikipedia.org/wiki/Metody_obliczania_pierwiastka_kwadratowego#Metoda_babilo.C5.84ska|metodą babilońską]] (kroki 1-3). Przerwij obliczenia, gdy różnica między kolejnymi przybliżeniami jest mniejsza niż $10^{-4}$. | ||
- | |||
- | //Wskazówka: Jako pierwsze oszacowanie możesz przyjąć $\sqrt{S} \approx S$ ;-) // | ||
- | |||
- | [size=10]Być może zastanawiasz się: //Po co ręcznie obliczać pierwiastek? Przecież można skorzystać z wbudowanej funkcji [[http://www.cplusplus.com/reference/cmath/sqrt/|sqrt()]]! // \\ | ||
- | Owszem, lecz jeśli nie potrzebujesz mega-dużej dokładności, to metodą babilońską uzyskasz wystarczająco dobry wynik o wiele, wiele szybciej.[/size] | ||
- | |||
- | ==== Zadanie SQUARE ==== | ||
- | |||
- | ([size=10]poziom trudności:[/size] {{stars>4/4}}) | ||
- | |||
- | Napisz program, który wypisze na ekran taki fragment: | ||
- | <code> | ||
- | 1 2 3 | ||
- | 2 3 1 | ||
- | 3 1 2 | ||
- | </code> | ||
- | Użyj pętli ''for''. | ||
- | |||
- | //Wskazówka: Przydatny może być operator "modulo"...// |