Blog literacki, portal erotyczny - seks i humor nie z tej ziemi
LEKCJA 13. Jak tworzyć w programie pętle i rozgałęzienia.
_______________________________________________________________
W trakcie tej lekcji:
1. Dowiesz się znacznie więcej o pętlach.
2. Przeanalizujemy instrukcje warunkowe i formułowanie warunków.
_______________________________________________________________
Zaczniemy tę lekcję nietypowo - od słownika, ponieważ dobrze
jest rozumieć dokładnie co się pisze. Tym razem słownik jest
trochę obszerniejszy. Pozwalam sobie przytoczyć niektóre słowa
powtórnie - dla przypomnienia i Twojej wygody. Do organizacji
pętli będą nam potrzebne następujące słowa:
[S!] conditional expressions - wyrażenia warunkowe
structural loops - pętle strukturalne
________________________________________________________________
if - jeżeli (poprzedza warunek do sprawdzenia);
else - a jeśli nie, to (w przeciwnym wypadku...);
for - dla;
while - dopóki (dopóki nie spełnimy warunku);
do - wykonaj, wykonuj;
break - przerwij (wykonanie pętli);
switch - przełącz;
case - przypadek, wariant (jedna z możliwości);
goto - idź do...
default - domyślny, (automatyczny, pozostały);
continue - kontynuuj (pętlę);
________________________________________________________________
UWAGA: W C/C++ nie stosuje się słowa THEN.
PĘTLA TYPU for.
Ogólna postać pętli for jest następująca:
for (W_inicjujące; W_logiczne; W_kroku) Instrukcja;
gdzie skrót W_ oznacza wyrażenie. Każde z tych wyrażeń może
zostać pominięte (patrz --> for(;;)).
Wykonanie pętli for przebiega następująco:
1. Wykonanie JEDEN raz WYRAŻENIA INICJUJĄCEGO.
2. Obliczenie wartości LOGICZNEJ wyrażenia logicznego.
3. Jeśli W_logiczne ma wartość PRAWDA (TRUE) nastąpi wykonanie
Instrukcji.
4. Obliczenie wyrażenia kroku.
5. Powtórne sprawdzenie warunku - czy wyrażenie logiczne ma
wartość różną od zera. Jeśli wyrażenie logiczne ma wartość zero,
nastąpi zakończenie pętli.
Warunek jest testowany PRZED wykonaniem instrukcji. Jeśli zatem
nie zostanie spełniony warunek, instrukcja może nie wykonać się
ANI RAZ.
Instrukcja może być INSTRUKCJĄ GRUPUJĄCĄ, składającą się z
instrukcji prostych, deklaracji i definicji zmiennych lokalnych:
{ ciąg deklaracji lub definicji;
ciąg instrukcji; }
Ogromnie ważny jest fakt, że C++ ocenia wartość logiczną
wyrażenia według zasady:
0 - FALSE, FAŁSZ, inaczej ZERO LOGICZNE jeśli WYRAŻENIE == 0 lub
jest fałszywe w znaczeniu logicznym;
1 - TRUE, PRAWDA, JEDYNKA LOGICZNA, jeśli wyrażenie ma DOWOLNĄ
WARTOŚĆ NUMERYCZNĄ RÓŻNĄ OD ZERA (!) lub jest prawdziwe w sensie
logicznym.
Przykład:
"Klasycznie" zastosowana pętla for oblicza pierwiastki
kwadratowe kolejnych liczb całkowitych.
#include
#include
void main()
{
int n;
for (n=0; n<=100; n++) printf("%f\t", sqrt(n));
getch();
}
Wyrażenie inicjujące może zostać pominięte. Innymi słowy zmienna
może zostać zainicjowana na zewnątrz pętli, a pętla przejmie ją
taką jaka jest w danym momencie. Przykładowo:
.....
{
float n;
n=(2*3)/(3*n*n - 1.234);
......
for (; n<=100; n++) printf("%f4.4\t", sqrt(n));
Przykład:
Warunek przerwania pętli może mieć także inny charakter. W
przykładzie pętla zostanie przerwana, jeśli różnica pomiędzy
kolejnymi pierwiastkami przekroczy 3.0.
void main()
{
float y=0, n=0;
for (; (sqrt(n)-y)<=3.0; n++)
{ y=sqrt(n);
printf("%f2.3\t", y);
}
getch();
}
UWAGA:
Sprawdź, czy nawias (sqrt(n)-y)<=3 można pominąć? Jaki jest
priorytet operatorów w wyrażeniach:
(sqrt(n)-y)<=3.0 i sqrt(n)-y<=3.0
Jaki będzie wynik? Dlaczego?
Przykład:
Instrukcja stanowiąca ciało pętli może być instrukcją pustą a
wszystkie istotne czynności mogą zostać wykonane w ramach samego
"szkieletu" for. Program przykładowy sprawdza ile kolejnych
liczb całkowitych trzeba zsumować by uzyskać sumę nie mniejszą
niż tysiąc.
void main()
{
float SUMA=0, n=0;
for (; SUMA < 1000; SUMA+=(++n));
printf("%f", n);
getch();
}
[???] CZY NIE MOŻNA JAŚNIEJ ???
________________________________________________________________
Można, ale po nabraniu wprawy takie skróty pozwolą Ci
przyspieszyć tworzenie programów. Zmniejszenie wielkości pliku
tekstowego jest w dzisiejszych czasach mniej istotne.
Rozszyfrujmy zapis SUMA+=(++n). Preinkrementacja następuje PRZED
użyciem zmiennej n, więc:
1. Najpierw ++n, czyli n=n+1.
2. Potem SUMA=SUMA+ (n+1).
Dla wyjaśnienia przedstawiam dwie wersje (obie z pętlą for):
void main() { void main()
float SUMA=0; { float SUMA=0, n=0;
int n; for (; SUMA < 1000; SUMA+=(++n)); }
clrscr();
for (n=0; SUMA<=1000; n++)
{
SUMA=SUMA+n;
}
}
________________________________________________________________
To jeszcze nie koniec pokazu elastyczności C/C++. W pętli for
wolno nam umieścić więcej niż jedno wyrażenie inicjujące i
więcej niż jedno wyrażenie kroku oddzielając je przecinkami.
flat a, b, c;
const float d=1.2345;
void main()
{
for (a=5,b=3.14,c=10; c; ++a,b*=d,c--)
printf("\n%f\t%f\t%f", a,b,c);
getch();
}
Zwróć uwagę, że zapisy warunku:
if (c)...; i if (c != 0)...;
są w C++ równoważne.
Przykład:
Program będzie pisał kropki aż do naciśnięcia dowolnego
klawisza, co wykryje funkcja kbhit(), będąca odpowiednikem
KeyPressed w Pascalu. Zapis !kbhit() oznacza "NIE NACIŚNIĘTO
KLAWISZA", czyli w buforze klawiatury nie oczekuje znak. Zwróć
uwagę, że funkcja getch() może oczekiwać na klawisz w
nieskończoność. Aby uniknąć kłopotliwych sytuacji, czasem
znacznie wygodniej jest zastosować kbhit(), szczególnie, jeśli
czekamy na DOWOLNY klawisz.
void main()
{
for (; !kbhit(); printf("."));
}
Przykład:
Wskaźnik w charakterze zmiennej roboczej w pętli typu for. Pętla
powoduje wypisanie napisu.
char *Ptr = "Jakis napis";
void main()
{
for (; (*Ptr) ;)
printf("%c",*Pt++);
getch();
}
AUTOMATYCZNE GENEROWANIE TABLIC W PĘTLI for
Na dyskietce znajdziesz jeszcze kilka przykładów FORxx.CPP
użycia pętli. A teraz, zanim będziemy kontynuować naukę -
przykładowy program do zabawy. Pętla for służy do wykrywania
zgodności klawisza z elementami tablicy TABL[]. W tablicy D[]
umieszczone zostały częstotliwości kolejnych dźwięków, które
program oblicza sam, wykorzystując przybliżony współczynnik.
[P030.CPP]
# include "conio.h"
# include "dos.h"
# include "math.h"
# include "stdio.h"
char TABL[27]={"zsxdcvgbhnjm,ZSXDCVGBHNJM<"};
char k;
float D[26];
int i;
void main()
{
clrscr();
printf("[A]- KONIEC, dostepne klawisze: \n");
printf(" ZSXDCVGBHNJM,i [Shift]");
D[0]=200.0;
for(i=1; i<26; i++) D[i]=D[i-1]*1.0577;
for (;;) //patrz przyklad {*}
{
k = getch();
for(i=0; i<27; i++)
{ if (k==TABL[i])
{ sound(D[i]); delay(100); nosound(); }
};
if (k=='a'|| k=='A') break; //Wyjście z pętli.
k = '0';
};
}
Po uruchomieniu programu klawisze działają w sposób
przypominający prosty klawiszowy instrument muzyczny.
Automatyczne zainicjowanie tablicy wielowymiarowej możemy
pozostawić C++. Wielkość tablicy może być znana na etapie
kompilacji programu, lub określona w ruchu programu.
C++ traktuje stałą (const) jako szczególny przypadek wyrażenia
stałowartościowego (ang. true constant expression). Jeśli
zadeklarowaliśmy zmienną wymiar jako stałą, możemy zastosować ją
np. do zwymiarowania tablicy TAB[]. Przykład poniżej przedstawia
takie właśnie zastosowanie stałych w C++.
[P031.CPP]
/* Inicjowanie tablicy przy pomocy stałej */
# include
main()
{
const int wymiar = 7; //Deklaracja stałej
char TAB[wymiar]; //Deklaracja tablicy
cout << "\n Wielkosc tablicy TAB[] wynosi: " << sizeof TAB;
cout << " bajtow.";
return 0;
}
Umożliwia to dynamiczne inicjowanie tablic pod warunkiem
rygorystycznego przestrzegania zasady, że do zainicjowana stałej
możemy zastosować wyłącznie wyrażenie stałowartościowe. .
[S] sizeof - wielkość w bajtach.
DANE PREDEFINIOWANE.
Dla ułatwienia życia programiście producenci kompilatorów C++
stosują stałe predefiniowane w plikach nagłówkowych, np.:
_stklen - wielkość stosu,
O_RDONLY - tryb otwarcia pliku "tylko do odczytu",
GREEN - numer koloru w palecie, itp., itp.
Predefiniowanych stałych możemy używać do deklarowania
indeksów/rozmiarów tablic.
PĘTLA TYPU while.
Pętlę typu while stosuje się na ogół "do skutku", tj. do momentu
spełnienia warunku, zwykle wtedy, gdy nie jesteśmy w stanie
przewidzieć potrzebnej ilości cykli. Konstrukcja pętli while
wygląda następująco:
while (Wyrażenie_logiczne) Instrukcja;
Jeśli Wyrażenie_logiczne ma wartość różną od zera, to zostanie
wykonana Instrukcja. Sprawdzenie następuje PRZED wykonaniem
Instrukcji, toteż Instrukcja może nie zostać wykonana ANI RAZU.
Instrukcja może być INSTRUKCJĄ GRUPUJĄCĄ.
Przykład
Stosujemy pętlę while do programu piszącego kropki (patrz
wyżej).
void main()
{
while (!kbhit()) printf(".");
}
Przykład
Stosujemy pętlę while w programie obliczającym sumę.
void main(){
float SUMA=0, n=0;
clrscr();
while (SUMA<1000) SUMA+=(++n);
printf("SUMA: %4.0f ostatnia liczba: %3.0f",
SUMA, n);
getch();
}
[P032.CPP]
char *Pointer1="Koniec napisu to \0, *Pointer==0 ";
char *Pointer2="Koniec napisu to \0, *Pointer==0 ";
void main(){
clrscr();
while (*Pointer1)
printf("%c", *Pointer1++);
printf("\nZobacz ten NUL na koncu lancucha znakow\n");
while (*Pointer2)
printf("%c", *Pointer2++);
printf("%d", *Pointer2);
getch();
}
PĘTLA do...while.
Konstrukcja dwuczłonowa do...while tworzy pętlę, która:
* jest wykonywana zawsze CO NAJMNIEJ JEDEN RAZ, ponieważ warunek
jest sprawdzany nie na wejściu do pętli, a na wyjściu z pętli;
* przerwanie pętli powodowane jest przez NIESPEŁNIENIE WARUNKU.
Schemat pętli do...while jest następujący:
do Instrukcja while (Wyrażenie_logiczne);
Instrukcja może być instrukcją grupującą.
Przykład:
void main()
{
do
{printf(".");}
while (!kbhit());
printf("Koniec petli....");
}
INSTRUKCJA WARUNKOWA if, if...else i if...else...if..
Instrukcja warunkowa ma postać:
if (Wyrażenie) Instrukcja;
if (Wyrażenie) Instrukcja1 else Instrukcja2;
Jeśli Wyrażenie ma wartość różną od zera (LOGICZNĄ bądź
NUMERYCZNĄ !) to zostanie wykonana Instrukcja1, w przeciwnym
razie wykonana zostanie Instrukcja2. Instrukcje mogą być
instrukcjami grupującymi. Słowa kluczowe if i else mogą być
stosowane wielokrotnie. Pozwala to tworzyć np. tzw. drzewa
binarne.
Przykład:
void main()
{
float a;
scanf("%f", &a);
if (a<0) printf("Ujemna!");
else if (a==0) printf("Zero!");
else printf("Dodatnia!");
}
Przykład:
if (a>0) if (a<100) printf("Dwucyfrowa"); else printf("100+");
inaczej:
if(a>0) {if(a<100) printf("Dwucyfrowa"); else printf("100+");}
Wyrażenie może zawierać operatory logiczne:
if (a>0 && a<100) printf("Dwucyfrowa"); else printf("100+");
Zapis 100+ oznacza "sto i więcej".
Przykład:
C++ pozwala na krótszy zapis instrukcji warunkowej:
(a>b)? MAX=a : MAX=b;
inaczej:
if (a>b) MAX=a; else MAX=b;
INSTRUKCJE break i continue.
Instrukcja break powoduje natychmiastowe bezwarunkowe
opuszczenie pętli dowolnego typu i przejście do najbliższej
instrukcji po zakończeniu pętli. Jeśli w pętli for opuścimy
wyrażenie logiczne, to zostanie automatycznie przyjęte 1. Pętla
będzie zatem wykonywana bezwarunkowo w nieskończoność. W
przykładzie poniżej nieskończoną pętlę przerywa po podaniu z
kalwiatury zera instrukcja break.
Przykład:
float a, sigma=0;
void main(){
for (;;)
{
printf("\n Podaj liczbe do sumowania\n");
scanf("%f", &a);
if (a==0) break;
sigma+=a;
printf("\n SUMA: %f",sigma);
}
printf("Nastapil BREAK");
getch();
}
Instrukcja continue.
Instrukcja continue powoduje przedwczesne, bezwarunkowe
zakończenie wykonania wewnętrznej instrukcji pętli i podjęcie
próby realizacji następnego cyklu pętli. Próby, ponieważ
najpierw zostanie sprawdzony warunek kontynuacji pętli. Program
z przykładu poprzedniego zmodyfikujemy w taki sposób, by
* jeśli liczba jest dodatnia - dodawał ją do sumy sigma;
* jeśli liczba jest ujemna - nie robił nic, pomijał bieżącą
pętlę przy pomocy rozkazu continue;
(Ponieważ warunek wejściowy pętli jest zawsze spełniony, to
pętlę zawsze uda się kontynuować.)
* jeśli liczba równa się zero - przerywał pętlę instrukcją break
Przykład:
float a, sigma=0;
void main()
{
for (;;)
{
printf("\n Sumuje tylko liczby dodatnie\n");
scanf("%f", &a);
if (a<0) continue;
if (a==0) break;
sigma+=a;
printf("\n SUMA: %f",sigma);
}
printf("Nastapil BREAK");
getch();
}
INSTRUKCJE switch i case.
Instrukcja switch dokonuje WYBORU w zależności od stanu
wyrażenia przełączającego (selector) jednego z możliwych
przypadków - wariantów (case). Każdy wariant jest oznaczony przy
pomocy stałej - tzw. ETYKIETY WYBORU. Wyrażenie przełączające
może przyjmować wartości typu int. Ogólna postać istrukcji jest
następująca:
switch (selector)
{
case STAŁA1: Ciąg_instrukcji-wariant 1;
case STAŁA2: Ciąg_instrukcji-wariant 2;
...............................
case STAŁAn: Ciąg_instrukcji-wariant n;
default : Ostatni_ciąg_instrukcji;
}
Należy podkreślić, że po dokonaniu wyboru i skoku do etykiety
wykonane zostaną również WSZYSTKIE INSTRUKCJE PONIŻEJ DANEJ
ETYKIETY. Jeśli chcemy tego uniknąć, musimy dodać rozkaz break.
[P033.CPP]
# define pisz printf //dla przypomnienia
# include
void main()
{
int Numer_Dnia;
pisz("\nPodaj numer dnia tygodnia\n");
scanf("%d", &Numer_Dnia);
switch(Numer_Dnia)
{
case 1: pisz("PONIEDZIALEK.");
case 2: pisz("WTOREK");
case 3: pisz("SRODA.");
case 4: pisz("CZWARTEK.");
case 5: pisz("PIATEK.");
case 6: pisz("SOBOTA.");
case 7: pisz("NIEDZIELA.");
default: pisz("\n *********************");
}
}
Zwróć uwagę, że w przykładzie wariant default zostanie wykonany
ZAWSZE, nawet jeśli podasz liczbę większą niż 7.
[P034.CPP]
# define pisz printf
# include
void main()
{
int Numer_Dnia;
pisz("\nPodaj numer dnia tygodnia\n");
scanf("%d", &Numer_Dnia);
switch(Numer_Dnia)
{
case 1: pisz("PON."); break;
case 2: pisz("WTOR"); break;
case 3: pisz("SRO."); break;
case 4: pisz("CZW."); break;
case 5: pisz("PIO."); break;
case 6: pisz("SOB."); break;
case 7: pisz("NIEDZ."); break;
default: pisz("\n ?????");
}
}
Instrukcja break przerywa wykonanie. Wariant default zostanie
wykonany TYLKO w przypadku podania liczby większej niż 7.
INSTRUKCJA POWROTU return.
Służy do zakończenia wykonania zawierającej ją funkcji i może
mieć postać:
return;
return stała;
return Wyrażenie;
return (wyrażenie);
Przykład:
Definiujemy funkcję _dodaj() zwracającą, poprzez instrukcję
return wartość przekazanego jej w momencie wywołania argumentu
powiększoną o 5.
float _dodaj(float x)
{
x+=5;
return x;
}
Funkcja _dodaj() zwraca wartość i nadaje tę wartość zmiennej
wynik zadeklarowanej nazewnątrz funkcji i znanej w programie
głównym. A oto program w całości.
[P035.CPP]
float funkcja_dodaj(float x)
{
x += 5;
return x;
}
float dana = 1, wynik = 0;
void main()
{
clrscr();
wynik = funkcja_dodaj(dana);
printf("%f", wynik);
}
INSTRUKCJA SKOKU BEZWARUNKOWEGO goto I ETYKIETY.
Składnia instrukcji skoku goto jest następująca:
goto Identyfikator_etykiety;
UWAGA: Po każdej etykiecie musi wystąpić CO NAJMNIEJ JEDNA
INSTRUKCJA. Jeśli etykieta oznacza koniec programu, to musi po
niej wystąpić instrukcja pusta. Instrukcja goto nie cieszy się
powodzeniem ani dobrą sławą (niesłusznie!). Ostrożne i umiejętne
jej stosowanie jeszcze nikomu nie zaszkodziło. Należy tu
zaznaczyć, że etykieta nie wymaga deklaracji.
Przykład:
Program poniżej generuje dźwięki i "odlicza".
[P036.CPP]
#include
#include
void main()
{
int czestotliwosc=5000, n=10, milisekundy=990;
printf("\n");
start:
{
sound(czestotliwosc);
delay(milisekundy);
nosound();
czestotliwosc/=1.2;
printf("%d\b", --n);
if (n) goto start; //petle strukturalne zrob sam(a)
}
koniec: ;
} // Tu jest instrukcja pusta.
[S!] DOS API function names - nazwy funkcji z interfejsu DOS
________________________________________________________________
sound - dźwięk;
delay - opóźnienie, zwłoka;
nosound - bez dźwięku (wyłącz dźwięk);
________________________________________________________________
[Z]
________________________________________________________________
1. Biorąc pod uwagę, że iloraz częstotliwości kolejnych dźwięków
jest stały tzn. Fcis/Fc=Ffis/Ff=....=const oraz, że oktawa to
podwojenie częstotliwości, opracuj program i oblicz
częstotliwości poszczególnych dźwięków.
2. Spróbuj zastosować w programie przykładowym kolejno pętle
for, while, do...while.
3. Zastosuj we własnym programie doświadczalnym instrukcję
switch.