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/02 20:14] pkleczek |
— (current) | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Pętla "while", debuggowanie ====== | ||
- | |||
- | ===== Cel laboratorium ===== | ||
- | |||
- | * Omówienie priorytetów operatorów. | ||
- | * Zapoznanie z pętlą ''while'' i ''do-while''. | ||
- | * Umiejętność posługiwania się //debuggerem//. | ||
- | |||
- | ===== 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. | ||
- | |||
- | 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 1** \\ | ||
- | Napisz program sprawdzający, czy dla zadanej przez użytkownika liczby $x$ zachodzi $4 < x < 6$. \\ | ||
- | Przetestuj swój program dla $x = 5$ oraz dla $x = 7$. | ||
- | ===== 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 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 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 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 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 i obcięcia w języku C]].// | ||
- | |||
- | ==== 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] | ||