Język C/C++

68
Język C/C++ Funkcje

description

Język C/C++. Funkcje. Funkcje - wstęp. Funkcję można uważać za operację zdefiniowaną przez programistę i reprezentowaną przez nazwę funkcji. Operandami funkcji są jej argumenty, ujęte w nawiasy okrągłe i oddzielone przecinkami. Typ funkcji określa typ wartości zwracanej przez funkcję. - PowerPoint PPT Presentation

Transcript of Język C/C++

Page 1: Język C/C++

Język C/C++

Funkcje

Page 2: Język C/C++

Funkcje - wstęp

• Funkcję można uważać za operację zdefiniowaną przez programistę i reprezentowaną przez nazwę funkcji. Operandami funkcji są jej argumenty, ujęte w nawiasy okrągłe i oddzielone przecinkami. Typ funkcji określa typ wartości zwracanej przez funkcję.

Page 3: Język C/C++

Kiedy używamy funkcji

• Często wykonujemy jakieś zadanie, np. obliczanie wartości sinus,

• zadanie jest bardzo trudne.

Page 4: Język C/C++

Metoda zstępująca w programowaniu

• Całe zadanie dzielimy na podzadania,

• podzadania są instrukcjami programu,

• są to instrukcje abstrakcyjne (nie występują w języku programowania),

• muszą mieć sformalizowaną postać (funkcje),

• w funkcjach należy określić jaki algorytm realizują i jak się komunikują z otoczeniem.

Page 5: Język C/C++

Deklaracja funkcji• Deklaracja funkcji ma postać:

– typ nazwa(deklaracje argumentów);

• Występujące tutaj trzy elementy: typ zwracany, nazwa funkcji i wykaz argumentów nazywane są łącznie prototypem funkcji.

• W języku C++ obowiązuje zasada deklarowania prototypu każdej funkcji przed jej użyciem. Prototyp funkcji o tym samym identyfikatorze może wystąpić w tym samym programie wiele razy (przeciążanie funkcji), natomiast brak prototypu funkcji wywoływanej w programie jest błędem syntaktycznym.

• Argumenty w deklaracji funkcji nazywa się również argumentami formalnymi lub parametrami formalnymi. Wykonanie instrukcji deklaracji funkcji nie alokuje żadnego obszaru pamięci dla parametrów formalnych.

Page 6: Język C/C++

Przykłady prototypów funkcji• int f1();• void f3();• void f3(void);• int* f5(int);• int (*fp6) (const char*, const char*);• extern double sqrt(double);• extern char *strcpy(char *to, const char *from);

• extern int strlen(const char *st);• extern int strcmp(const char *s1, const char *s2);

• int printf(const char *format, ...);

Page 7: Język C/C++

Wywołanie funkcji• Wywołanie funkcji jest poleceniem obliczenia wartości

wyrażenia, zwracanej przez nazwę funkcji. Instrukcja wywołania ma składnię:– nazwa (argumenty aktualne);

• gdzie nazwa jest zadeklarowaną wcześniej nazwą funkcji, argumenty aktualne są wartościami argumentów formalnych, zaś para nawiasów okrągłych () jest operatorem wywołania.

• Liczba, kolejność i typy argumentów aktualnych powinny dokładnie odpowiadać zadeklarowanym argumentom formalnym. Przy niezgodności typów argumentów kompilator stara się wykonać konwersje niejawne; jeżeli nie może dokonać sensownej konwersji, sygnalizuje błąd.

Page 8: Język C/C++

Co się dzieje przy wywoływaniu funkcji ?

• Zastosowanie operatora wywołania do nazwy funkcji powoduje alokację pamięci dla argumentów formalnych i przekazanie im wartości argumentów aktualnych. Od tej chwili argumenty formalne stają się zmiennymi lokalnymi o wartościach inicjalnych równych przesłanym do nich wartościom argumentów aktualnych.

• Zasadniczym sposobem przekazywania argumentów do funkcji jest przekazywanie przez wartość: do każdego argumentu formalnego jest przesyłana kopia argumentu aktualnego. Ponieważ argumenty formalne po wywołaniu stają się zmiennymi lokalnymi funkcji, zatem wszelkie wykonywane na nich operacje nie zmieniają wartości argumentów aktualnych.

• Wyjątkiem od tej zasady jest przesłanie do funkcji adresów argumentów aktualnych za pomocą wskaźników lub referencji - później.

Page 9: Język C/C++

Definicja funkcji• Składnia definicji funkcji jest następująca:

typ nazwa (deklaracje argumentów)

{

instrukcje

}• Definicja podaje typ zwracany przez funkcję, jej nazwę oraz argumenty formalne.

Argumentami formalnymi funkcji mogą być zmienne wszystkich typów podstawowych, struktury, unie oraz wskaźniki i referencje do tych typów, a także zmienne typów definiowanych przez użytkownika. Nie mogą być nimi tablice, ale mogą być wskaźniki do tablic (jeśli są tablice, to są traktowane jako wskaźniki).

• Typ funkcji nie może być typem tablicowym ani funkcyjnym, ale może być wskaźnikiem do jednego z tych typów. Zarówno typ zwracany, jak i typy argumentów muszą być podawane w postaci jednoznacznych identyfikatorów. Identyfikatory mogą być nazwami typów wbudowanych (np. char) lub zdefiniowanych wcześniej przez użytkownika. Inaczej mówiąc, w nagłówku funkcji nie mogą występować definicje typów.

Page 10: Język C/C++

Ciało funkcji• Instrukcje w bloku (nazywanym również ciałem funkcji)

mogą być instrukcjami deklaracji. Ostatnią instrukcją przed nawiasem klamrowym zamykającym blok funkcji musi być instrukcja return; jedynie dla funkcji typu void instrukcja return; jest opcją. Zatem definicja– int f() { };

• jest błędna, natomiast definicja– void f() { };

• jest poprawna.

Page 11: Język C/C++

Instrukcja return• Instrukcja return; występuje często w postaci: return wyrażenie;

• gdzie wyrażenie określa wartość zwracaną przez funkcję. Jeżeli typ tego wyrażenia nie jest identyczny z typem funkcji, to kompilator będzie próbował osiągnąć zgodność typów drogą niejawnych konwersji. Jeżeli okaże się to niemożliwe, to kompilacja zostanie zaniechana. Zgodność typu zwracanego z zadeklarowanym typem funkcji można również wymusić drogą konwersji jawnej.

• W bloku funkcji może wystąpić więcej niż jedna instrukcja (instrukcje warunkowe) return;.

Page 12: Język C/C++

Uwagi

• Deklaracje argumentów muszą podawać oddzielnie typ każdego argumentu; nazwy argumentów są opcjonalne.

• Definicja, która nie zawiera nazw argumentów, a jedynie ich typy, jest syntaktycznie poprawna.

Page 13: Język C/C++

Przykład#include <iostream.h>

//deklaracja funkcji - prototyp

int dods(int, int);

int main() {

int i, j, k;

cout <<”Wprowadz dwie liczby typu int: ”;

cin >> i >> j;

cout << '\n';

k = dods (i,j); //wywolanie funkcji

cout << ”i=” << i << ”\tj=” << j << '\n';

cout << ”dods(i,j)= ”<< k << '\n';

return 0;

}

int dods (int n, int m)

{

if (n + m > 10) return n + m;

else return n;

}

Page 14: Język C/C++

Wywołanie funkcji• Wywołanie funkcji zawiesza wykonanie funkcji

wołającej i powoduje zapamiętanie adresu następnej instrukcji do wykonania po powrocie z funkcji wołanej. Adres ten, nazywany adresem powrotnym, zostaje umieszczony w pamięci na stosie programu (ang. run-time stack).

• Wywołana funkcja otrzymuje wydzielony obszar pamięci na stosie programu, nazywany rekordem aktywacji lub stosem funkcji. W rekordzie aktywacji zostają umieszczone argumenty formalne, inicjowane jawnie w deklaracji funkcji, lub niejawnie przez wartości argumentów aktualnych.

Page 15: Język C/C++

Parametry domyślne• Jawne inicjowanie argumentów w deklaracji (nie w definicji) funkcji

można traktować jako przykład przeciążenia funkcji; tematem tym zajmiemy się później bardziej szczegółowo. Weźmy następujący przykład: w prototypie funkcji, która symuluje ekran monitora, wprowadźmy inicjalne wartości domyślne dla szerokości, wysokości i tła ekranu

• char* ekran(int x=80, int y=24, char bg = ' ');• Wprowadzone wartości początkowe argumentów x, y oraz bg są

domyślne w tym sensie, że jeżeli w wywołaniu funkcji nie podamy argumentu aktualnego, to na stosie funkcji zostanie “położona” wartość domyślna argumentu formalnego.

• Jeżeli teraz zadeklarujemy zmienną char* kursor, to wywołanie• kursor = ekran();• jest równoważne wywołaniu• kursor = ekran(80, 24, ' ');

Page 16: Język C/C++

Parametry domyślne• Jeżeli w wywołaniu podamy inną od domyślnej wartość argumentu,

to zastąpi ona wartość domyślną, np. wywołanie– kursor = ekran(132);

• jest równoważne wywołaniu– kursor = ekran(132, 24, ' ');

• zaś wywołanie– kursor = ekran(132, 66);

• jest równoważne wywołaniu– kursor = ekran(132, 66, ' ');

• Składnia ostatniego wywołania pokazuje że nie można podać wartości pierwszego z prawej argumentu nie podając wszystkich wartości po lewej.

Page 17: Język C/C++

Parametry domyślne

• Deklaracja funkcji nie musi zawierać wartości domyślnych dla wszystkich argumentów, ale podawane wartości muszą się zaczynać od skrajnego prawego argumentu, np.– char* ekran( int x, int y, char bg = ' ' );– char* ekran( int x, int y = 24, bg = ' ' );

• Wobec tego deklaracje– char* ekran (int x = 80, int y, char bg = ' ');– char* ekran ( int x, int y = 24, char bg );

• są błędne.

Page 18: Język C/C++

Przekazywanie argumentów przez wartość

• W języku C++ przekazywanie argumentów przez wartość jest domyślnym mechanizmem językowym. Przy braku takiego mechanizmu każda zmiana wartości argumentu formalnego nie poprzedzonego modyfikatorem const wywołałaby taką samą zmianę argumentu aktualnego. Założenie to zostało podyktowane tym, że ewentualne zmiany wartości argumentów aktualnych, będące wynikiem wykonania jakiejś funkcji, są na ogół traktowane jako niepożądane efekty uboczne.

Page 19: Język C/C++

Przekazywanie argumentów przez wartość

#include <iostream.h>

Using namespace std;

int imax(int x, int y);

int main() {

float zf = 35.7;

double zd = 11.0;

int ii;

ii = imax( zf, zd );

cout << ”ii =”<< ii << endl;

return 0;

}

int imax(int x, int y)

{

if (x > y) return x;

else return y;

}

Page 20: Język C/C++

Przekazywanie argumentów przez wartość

• Przekazywanie argumentów przez wartość nie będzie dogodnym mechanizmem w dwóch przypadkach:– gdy wartości argumentu aktualnego muszą być przez funkcję

zmodyfikowane,

– gdy przekazywany argument reprezentuje duży obszar pamięci (np. tablica, struktura).

• Dla tablic jest stosowany mechanizm, który nakazuje kompilatorowi przekazanie argumentów aktualnych w postaci adresu pierwszego elementu tablicy zamiast kopii całej tablicy. Natomiast w pierwszym przypadku należy znaleźć własne rozwiązanie.

Page 21: Język C/C++

Przekazywanie argumentów przez wartość

• Klasycznym przykładem nieprzydatności przekazywania argumentów przez wartość jest operacja zamiany wartości dwóch zmiennych. Funkcjavoid zamiana(int x, int y)

{

int pomoc = y;

y = x;

x = pomoc;

}

Page 22: Język C/C++

Przekazywanie argumentów przez wskaźnik

• Pierwszy z nich polega na zastosowaniu wskaźników. Sposób ten ilustruje poniższy przykład.

#include <iostream>

using namespace std;

void zamiana1 (int*, int* );

int main() {

int i = 10;

int j = 20;

zamiana1( &i, &j );

cout <<"Po zamianie i=" << i << "\tj="<< j << endl;

return 0;

}

void zamiana1(int* x, int* y)

{

int pomoc = *y;

*y = *x;

*x = pomoc;

}

Page 23: Język C/C++

Przekazywanie argumentów przez adres

• Poprzedni program wydaje się być mało czytelny. Alternatywne rozwiązanie tego samego zadania wykorzystuje referencje zamiast wskaźników, dając prostszą i bardziej czytelną notację.#include <iostream>

Using namespace std;

void zamiana2 (int& i, int& j );

int main() {

int i = 10;

int j = 20;

zamiana2( i, j );

cout <<"Po zamianie i=" << i << "\tj="<< j << endl;

return 0;

}

void zamiana2(int& x, int& y)

{

int pomoc = y;

y = x;

x = pomoc;

}

Page 24: Język C/C++

Wskaźniki do funkcji• Tak jak nazwa tablicy, nazwa funkcji jest stałym

wskaźnikiem. Ponieważ nazwa funkcji jest stałym wskaźnikiem, to wskaźnik do funkcji jest wskaźnikiem do stałego wskaźnika, który jest nazwą funkcji.

• int f(int); // deklaracja funkcji f

• int (*pf) (int); • // deklaracja wskaźnika do funkcji typu int funkcja(int)

• pf = &f; // adres f przypisany jest do pf

Page 25: Język C/C++

Przykład#include <iostream>

using namespace std;

int suma(int (*) (int), int); // dwa parametry: wskaźnik do funkcji

int square(int);

int cube(int);

main()

{

cout << suma(square,5) << endl;

cout << suma(cube,5) << endl;

}

int suma(int (*pf)(int k), int n) // k nie jest używane ale wymagane

{

int s = 0;

for (int i =0; i <= n; i++)

s += (*pf)(i);

return s;

}

Page 26: Język C/C++

Przykładint square(int k)

{

return k*k;

}

int cube(int k)

{return k*k*k;

}

Page 27: Język C/C++

Zapowiedź funkcji, zmienne globalne// zmienne globalne

#include <iostream>

#include <math.h>

using namespace std;

int a[10], n; float war;

void wprowadz();

void wariancja();

main()

{

wprowadz();

wariancja();

cout <<"wariancja= "<<war;

getchar();

}

void wprowadz()

{

cout<<"n="; cin>>n;

for (int i=1; i<=n; i++)

{

cout <<"a["<<i<<"]="; cin >>a[i];

}

}

//************************************************

float srednia()

{

float s=0;

for (int i=1; i<=n; i++)

s+=a[i];

return s/=n;

}

void wariancja()

{

float sr=srednia();

war=0;

for (int i=1; i<=n; i++)

war=war+(a[i]-sr)*(a[i]-sr);

war=sqrt(war/n);

}

Page 28: Język C/C++

Przeciążanie funkcji

• Funkcje można w C++ (ale nie w C) przeciążać (przeładowywać).

• Oznacza to sytuację, gdy w tym samym zakresie są widoczne różne definicje funkcji o tej samej nazwie.

• wywołania funkcji muszą się różnić, tak by można było jednoznacznie wybrać jedną z nich na podstawie wywołania.

Page 29: Język C/C++

Przeciążanie funkcji

• Sygnatury nie różnią się:– int f(int i); – float f(int j);– int f(int k;int l=0);

• Sygnatury różnią się:– int f(int i); – int f(float i);– int f(int i; int j);

Page 30: Język C/C++

Przykład

#include <iostream>

#include <conio.h>

using namespace std;

int f(int i)

{

return i;

}

int f(float i)

{

return 2*i;

}

int main(int argc, char* argv[])

{

cout<<"Pierwsze wywolanie: "<<f(1)<<endl;

cout<<"drugie wywolanie: "<<f((float)1);

getch();

return 0;

}

Page 31: Język C/C++

Kiedy przeciążać funkcje• Przeciążanie stosujemy wtedy, gdy funkcje realizują podobny

algorytm, ale różny algorytm. • Przykładem może być maksimum dla jednej, dwu i trzech

liczb.#include <iostream>

#include <conio.h>

using namespace std;

int max(int i) {

return i;}

int max(int i, int j){

return (i>j?i:j);}

int max(int i, int j, int k){

return ((i>j)&&(i>j)?i:(j>k)?j:k);}

int main(int argc, char* argv[])

{

cout<<"max(1)="<<max(1)<<endl;

cout<<"max(1,2)="<<max(1,2)<<endl;

cout<<"max(1,2,3)="<<max(1,2,3)<<endl;

getch();

return 0;

}

Page 32: Język C/C++

Podsumowanie zagadnień związanych z funkcjami

• Co nam daje funkcja:– Wydzielamy kod,

– Łatwiej zarządzać zmiennymi (funkcje korzystają ze zmiennych lokalnych),

• Budowa funkcji:– Nagłówek funkcji,

– Ciało funkcji

• Kiedy używamy funkcji:– Trudny problem,

– Powtarzalne wykorzystanie algorytmu,

– Jeśli coś wykonujemy, to funkcja zwraca wartość typu void, w przeciwnym razie (coś obliczamy), zwraca pewną wielkość o określonym typie.

Page 33: Język C/C++

Przykład pokazujący na łatwiejsze zarządzanie zmiennymi

#include <iostream.h>

#include <conio.h>

using namespace std;

void cos(int i)

{

int j=i*2; //zmienne i i j nie mają ZADNEGO związku ze zmiennymi i i j w funkcji głównej

return j;

}

int main(int argc, char* argv[])

{

int i, j;

j=9; i=10;

cos(2);

j=i; i=i+2; //zmienne i i j z cos nie licza sie

getch();

return 0;

}

Page 34: Język C/C++

Podsumowanie zagadnień związanych z funkcjami

• Bardzo ważnym elementem w projektowaniu funkcji jest określenie sposobu komunikacji funkcji z programem.

• Są cztery różne możliwości:– Zmienne globalne, – Przez wartość, – Przez wskaźnik, – Przez referencję.

Page 35: Język C/C++

Komunikacja przez zmienne globalne

• Wtedy, gdy mamy w programie bardzo ważne zmienne, z których korzystamy wszędzie,

• Wada – niebezpieczeństwo wystąpienia efektów ubocznych (ostatni przykład ale zmienne i i j są globalne),

• Zaleta – łatwy dostęp do danych, krótszy tekst programu.

• Mimo zalet zmienne globalne należy używać bardzo rzadko, najlepiej wcale.

Page 36: Język C/C++

Przekazywanie przez wartość• Kiedy używamy:

– Gdy chcemy do funkcji przekazać wartość jakiejś danej,

• Czym jest parametr formalny:– <typ> identyfikator

• Czym jest parametr aktualny:– Wyrażeniem

• Przykłady:– int f(int);– f(1), f(1+1), f(i), f(2+i), f(f(8))

Page 37: Język C/C++

Przykład#include <iostream>

#include <conio.h>

using namespace std;

//************************************************

float moja_potega(float x, int n)

{

float il=1.;

for (int i=0;i<n;i++)

il=il*x;

return il;

}

//************************************************

main()

{

cout <<"4^5="<<moja_potega(4,5)<<endl;

float x=2.; int i=4;

cout <<"2^4="<<moja_potega(x,i);

getchar();

}

Page 38: Język C/C++

Przekazywanie danych przez referencję

• Kiedy używamy:– Kiedy chcemy zmienić wartość zmiennej

(najczęściej), – Możemy także w ten sposób przekazywać dane (gdy

tylko przekazujemy, to nie jest zalecane),

• Schemat przekazywania:– Nagłówek funkcji: int f(int &n)– Wywołanie: f(liczba)– Niepoprawne wywołanie: f(i+4), f(5)

Page 39: Język C/C++

Obliczanie wariancji - przekazywanie danych przez referencje – uwaga na tablice

#include <iostream>

#include <conio.h>

using namespace std;

void wprowadz(int a[10], int &n)

{

cout<<"n="; cin>>n;

for (int i=1; i<=n; i++)

{

cout <<"a["<<i<<"]="; cin >>a[i];

}

}

//*************************************************

float srednia(int a[10], int n)

{

float s=0;

for (int i=1; i<=n; i++)

s+=a[i];

return s/n;

}

//************************************************

//float wariancja(int *a, int n)

void wariancja(int a[10], int n, float &war)

{

float sr=srednia(a,n);

war=0;

for (int i=1; i<=n; i++)

war=war+(a[i]-sr)*(a[i]-sr);

war=war/n;

// return (war/n);

}

//************************************************main(){ int n,a[10]; float war; wprowadz(a,n); wariancja(a,n,war); cout <<"wariancja= "<<war; //cout<<"wariancja="<<wariancja(a,n); getchar();}

Page 40: Język C/C++

Przekazywanie danych przez wskaźniki

• Kiedy używamy:– Kiedy chcemy zmienić wartość zmiennej

(najczęściej), – Możemy także w ten sposób przekazywać dane (gdy

tylko przekazujemy, to nie jest zalecane),

• Schemat przekazywania:– Nagłówek funkcji: int f(int *n)– Wywołanie: f(&liczba)– Niepoprawne wywołanie: f(i+4), f(5)

Page 41: Język C/C++

Obliczanie wariancji - przekazywanie danych przez wskaźniki– uwaga na tablice

#include <iostream.h>

#include <math.h>

void wprowadz(int *a, int *n)

{

cout<<"n="; cin>>*n;

for (int i=0; i<*n; i++)

{

cout <<"a["<<i<<"]="; cin >>a[i];

}

}

//************************************************

float wariancja(int *a, int n)

{

float sr=0;

for (int i=0; i<n; i++)

sr+=a[i];

sr/=n;

float war=0;

for (int i=0; i<n; i++)

war=war+(a[i]-sr)*(a[i]-sr);

//war=sqrt(war/*n);

return sqrt(war/n);

}

//************************************************

main()

{ int n,a[10];

float war;

wprowadz(a,&n);

cout <<"wariancja= "<<wariancja(a,n);

getchar();

}

Page 42: Język C/C++

Co jeszcze dają nam funkcje?

• Możliwość wykonywania różnych algorytmów dla danych (to, że wykonujemy TEN SAM algorytm dla różnych danych, to oczywiste),

• Przeciążania funkcji,

• Stosowania ustawień domyślnych (parametry domyślne).

Page 43: Język C/C++

W jaki sposób dokonywać podziału całego algorytmu na

funkcje?

(trochę już było)

Page 44: Język C/C++

Algorytm abstrakcyjnyW momencie rozwiązywania złożonego

problemu należy skupić się na wyrażeniu rozwiązania w kategoriach języka naturalnego. Takie rozwiązanie będzie nazywane algorytmem abstrakcyjnym. Wyraża on tylko ogólną strategię rozwiązania problemu wraz z ogólną strukturą rozwiązania, które chcemy otrzymać. Algorytm abstrakcyjny zawiera instrukcje abstrakcyjne i dane abstrakcyjne (tzn. takie, które nie mają swoich odpowiedników w danym języku programowania).

Page 45: Język C/C++

Metoda zstępującaTworzenie programu polega więc na określaniu

kolejnych uściśleń, tak aby w kolejnych krokach instrukcje i dane abstrakcyjne wyrażać w kategoriach wybranego języka programowania. Można więc powiedzieć, że kolejne uściślenia prowadzą do programu na niższym programie abstrakcji (instrukcje i dane abstrakcyjne za każdym razem są coraz bliższe danemu językowi programowania). Taki proces kończy się w momencie wyrażenia całego rozwiązania w wybranym języku programowania. Taki stopniowy rozkład problemu i jednoczesne uściślanie rozwiązania nosi nazwę metody zstępującej. Ważne jest, by wybrany język programowania pozwalał na zapis takiej drogi otrzymania rozwiązania.

Page 46: Język C/C++

Projektowanie metodą zstępującąSam proces projektowania programu powinien

uwzględniać możliwości (instrukcje danego j. programowania). Należy jednak używać możliwie najdłużej symboliki naturalnej dla danego problemu i odkładać jak najdłużej niezbędne decyzje zależne od szczegółów komputera i samego języka programowania. Dzięki temu zmiana języka lub komputera wymaga możliwie małych zmian. Także z pewnych błędnych rozwiązań można się wycofać stosunkowo łatwo (czasem może być potrzebny powrót do pierwszego szkicu rozwiązania).

Page 47: Język C/C++

Sprawdzanie poprawności• Ważną cechą stopniowego uściślania programu jest

to, że równolegle można sprawdzać poprawność tworzonego programu.

• Należy dowodzić poprawności każdej kolejnej wersji programu (oczywiście na pewnym poziomie abstrakcji).

• Dowody to nie są operacje w sensie matematycznym. Raczej zdroworozsądkowym, logicznym.

Page 48: Język C/C++

Sprawdzanie poprawności• Na każdym kolejnym poziomie dowodu poprawności zakłada

się, że dalsze uściślenia prowadzące do kolejnego niższego poziomu abstrakcji (bliższego danemu językowi programowania) zachowują odpowiednie kryteria poprawności.

• Oznacza to, że zaczynamy od pierwszego, najbardziej abstrakcyjnego etapu, i dowodzimy jego poprawności, zakładając, że instrukcje i operacje abstrakcyjne są uściślone poprawnie.

• Ponieważ programy abstrakcyjne są znacznie prostsze i krótsze więc dowody poprawności są również krótkie i mniej uciążliwe. W kolejnym etapie stosujemy to samo podejście do poszczególnych instrukcji abstrakcyjnych kończąc w chwili, gdy każdą instrukcję zapiszemy w wybranym języku programowania.

Page 49: Język C/C++

Przykład – szukamy liczb, których kwadraty są palindromami

• Def. Palindromem jest taki ciąg znaków, który tak samo czyta się od początku i od końca, np. 121, oko.

• Rozwiązanie:

• Uściślenie: przyjmiemy, że będziemy rozpatrywać pewien zakres liczb: 1..N. Zauważmy, także, że łatwiej jest sprawdzić, czy liczba jest palindromem, niż to, że jest kwadratem. Dlatego też problem sprowadzimy do sytuacji, gdy generujemy liczby, które są kwadratami liczb z przedziału 1..N, a następnie sprawdzamy, czy są one palindromami (można np. przeglądać wszystkie liczby będą kwadratami, a następnie sprawdzać, czy są one kwadratami i ew. wybierać palindromy).

Page 50: Język C/C++

Algorytm abstrakcyjnyNajbardziej abstrakcyjny algorytm ma postać:

Algorytm I– n=0;– powtarzaj– n=n+1;– generowanie kwadratu;– if (dziesiętna reprezentacja kwadratu jest

palindromem) – pisz(n);– tak długo jak n<>N;

Page 51: Język C/C++

Algorytm abstrakcyjny, cd• Aby zbliżyć się do języka programowania

zauważmy, że należy wprowadzić zmienne reprezentujące wynik jednego kroku obliczeń. Analiza programu pokazuje, że możemy mieć do czynienia z następującymi zmiennymi:

Zmienna całkowita s, której przypiszemy kwadrat, zmienna tablicowa d dla reprezentacji dziesiętnej

liczby s, zmienna boolowska p dla wyniku testu, czy liczba

jest palindromem.

Page 52: Język C/C++

Algorytm abstrakcyjny, cdUwzględniając te oznaczenia możemy Algorytm I

zapisać w postaci: Algorytm II

• n=0;

• powtarzaj– n=n+1;

– s=n*n;

– d=reprezentacja dziesiętna liczby s;

– p=d jest palindromem;

– if (p) pisz(n);

• tak długo jak n<>N;

Page 53: Język C/C++

Algorytm abstrakcyjny, cd• Dalej należy rozważyć dwie abstrakcyjne

instrukcje. Są to d=reprezentacja dziesiętna liczby s; p=d jest palindromem;

• Pierwszą z nich można zapisać następująco:– d=reprezentacja dziesiętna liczy s

d=reprezentacja dziesiętna liczby n2, a to oznacza, że

– np. n=7, mamy n2=49 i reprezentacja tej liczby jest postaci: d[1]=9, d[2]=4, gdzie L=2.

L

1k

1k2 10*]k[dn

Page 54: Język C/C++

Algorytm abstrakcyjny, cd• Drugą z nich możemy następująco

zinterpretować: ponieważ aby instrukcja p:=d jest palindromem powinna zachodzić następująca relacja:

)])1k(L[d]k[d()2/)1L(k1(kp

Page 55: Język C/C++

Algorytm abstrakcyjny, cdPodane wcześniej instrukcje

d=reprezentacja dziesiętna liczby s; p=d jest palindromem;

uściśla się za pomocą instrukcji:– d=reprezentacja dziesiętna liczby s;

oraz – p=d jest palindromem;

Page 56: Język C/C++

Algorytm abstrakcyjny, cd• Pierwszą z nich uściśla się następująco: Algorytm IVa

L=0;

do {

L=L+1;

d[L]=s % 10;

s=s / 10;

} while (s!=0);

Page 57: Język C/C++

Algorytm abstrakcyjny, cd

• Uściślimy teraz instrukcję „p=d jest palindromem”, można ją zapisać następująco:i=1; j=L;

do { p=(d[i]==d[j]);

i=i+1; j=j-1;

}while ((i<j) && p)

Page 58: Język C/C++

Podsumowanie - cały program (zapis miał

być z założenia bardzo podobny do języka C/C++ ale nie koniecznie taki, by kompilacja była poprawna – pseudokod)

• n=0;

• do {– n=n+1; s=n*n;

– L=0; // wyznaczenie d (reprezentacji dziesiętnej)

– do {

– L=L+1;d[L]=s %10; s=s / 10;

– }while (s!=0);

– i=1; j=L; // czy d jest palindormem?

– do {• p=(d[i]==d[j]);• i=i+1; j=j-1;

– } while ((i<j) && p)

– if (p) pisz(n);

• } while (n!=N);

Page 59: Język C/C++

Przykład Sortowania tablic Rozważamy porządek sortowania malejący.

• Sortowanie przez wybór. – Dana jest tablica A z granicami indeksu 0..N-1 i

składowymi skalarnymi. – Zadanie – poprzestawiać elementy tablicy tak,

by na końcu zachodziło:• A[0]<=A[2]<=...<=A[N-1] - otrzymana tablica w

takiej sytuacji będzie posortowana.

Page 60: Język C/C++

Przykład Sortowania tablic• Idea algorytmu polega na tym, by tablicę A

podzielić na części A[0..i-1] złożona z elementów A[0], ... A[i-1] i część A[i..N-1] złożoną z elementów A[i],..., A[N-1].

• Na początku część A[0..i-1] jest pusta.

• Dla pierwszego kroku część A[0..i-1] jest posortowana i w każdym kroku tę część rozszerza się o kolejny jeden element.

• Na końcu algorytmu część A[0..N-1] jest posortowana.

Page 61: Język C/C++

Przykład Sortowania tablic - cd• Początkowy szkic programu można

przedstawić następująco:– for(i=0; i<N; i++)

• Przestawienie wartości A[i..N] umieszczające najmniejszą wartość w A[i]

• Po wykonaniu tej pętli tablica A[0..N-1] ma być posortowana, tzn. musi zachodzić: p,q ((0<=p<q<=N-1)=>(A[p]<=A[q]) – to jest

stan oczekiwany.

Page 62: Język C/C++

Przykład Sortowania tablic - cdUściślimy teraz instrukcję

– przestawienie wartości A[i..N-1] umieszczające najmniejszą w A[i]

tak, by rzeczywiście przestawiała elementy. • Można to zapisać za pomocą następujących

instrukcji:• m=i;• for(j=i+1;j<N;j++)

– if (A[i]<A[m]) m=j• Zamiana A[i] i A[m]

Page 63: Język C/C++

Przykład Sortowania tablic - cd

Natomiast instrukcję – Zamiana A[i] i A[m]

Możemy zapisać następująco:– t=A[i]; – A[i]=A[m]; – A[m]=t;

Page 64: Język C/C++

Program (zasadnicza część)for (i=0; i<N; i++)

{m=i;

for (j=i+1; j<N; j++)

{

if (A[i]<A[m]) {

m=j;

t=A[i];

A[i]=A[m];

A[m]=t;

}}

}

Page 65: Język C/C++

Uwagi dotyczące zasad wyboru funkcji

1. Funkcja powinna być „logiczna” w tym sensie, że powinna wykonywać pewne, ściśle określone zadanie, powinno ono wynikać z algorytmu,

2. Funkcja powinna wykonywać wszystkie operacje powiązane ze sobą logicznie,

3. Funkcja powinna komunikować się za pomocą parametrów (zmienne globalne używamy w ostateczności, a najlepiej wcale),

4. Należy dokładnie rozważyć, co funkcja robi i jak się komunikuje – jakie dane potrzebuje, co powinna obliczać:

1. Gdy potrzebuje danych – przekazywanie przez wartość, 2. Gdy oblicza parametr – przez wskaźnik i przez referencję, 3. Gdy oblicza jedną wartość, to używamy return,4. Gdy obliczamy dwie lub więcej wartości, to obliczane wielkości

przekazujemy przez parametry.

Page 66: Język C/C++

Uwagi dotyczące metody zstępującej i wstępującej

• Metoda zstępująca (od ogółu do szczegółu):– Wtedy, gdy znamy całe zagadnienie, dla którego

piszemy program, – Dzielimy całe (duże) zagadnienie na mniejsze

podzagadnienia, – Mniejsze podzagadnienia dzielimy na jeszcze

mniejsze, – Iteracje powtarzamy tak długo, aż wszystkie

operacje będą mogły być zapisane za pomocą elementów danego języka.

Page 67: Język C/C++

Przykłady problemów, które możemy projektować z wykorzystaniem metody

zstępującej• Są to problemy, które są w sposób zamknięty

zdefiniowane. • Czyli są to np. problemy opisane za pomocą

odpowiednich przepisów prawnych, wynikają ze znanych procedur obowiązujących w firmie itd.,

• Przykładem takich problemów mogą być systemy płacowe, kadrowe, finansowe.

Page 68: Język C/C++

Przykłady problemów, które możemy projektować z wykorzystaniem

metody wstępującej

• Są to problemy, które nie są zdefiniowane w sposób ściśle określony,

• Czyli w trakcie eksploatacji mogą pojawiać się nowe potrzeby, które nie dyskwalifikują naszego rozwiązania,

• Przykładem takich problemów mogą być np. aplikacje typu system operacyjny, kalkulator.