• Nie Znaleziono Wyników

Języki programowania wskazówki do zadań z listy II, 30.03.2020

N/A
N/A
Protected

Academic year: 2021

Share "Języki programowania wskazówki do zadań z listy II, 30.03.2020"

Copied!
3
0
0

Pełen tekst

(1)

Języki programowania

wskazówki do zadań z listy II, 30.03.2020

W zadaniach z listy II koncentrujemy się na różnych metodach implementacji elementarnych struktur danych, pełniących rolę kontenerów informacji. Nauczymy się w ten sposób programowania z użyciem dynamicznych list i dynamicznie deklarowanych tablic.

Każda z interesujących nas struktur przechowuje ciągi danych określonego typu, np. liczbyintlubfloat, wielopolowe rekordy danych itp. Ważny jest sposób dostępu do tych danych. Podstawowe operacje na konte- nerach to:

push zapisująca nowy element w kontenerze

pop zwracająca wartość pojedynczego elementu z kontenera, jednocześnie usuwająca go z niego

peek operacja podglądu wartości kolejnego elementu do pobrania, jednak pozostawiająca go w kontenerze

empty funkcja logiczna testująca czy wskazany kontener jest aktualnie pusty.

Oto struktury, które będziemy tworzyć:

1. Stosy są strukturami działającymi wg schematu LIFO (last in first out). Operacjapopzwraca ten obiekt, który był ostatnim zapisanym w stosie. Nazwa kojarzy się ze stosem kartek papieru, którym operujemy w ten sposób, że nowe kartki z notatkami kładziemy na wierzchu, a zabieramy kolejne notatki pojedynczo także z wierzchu stosu.

2. Kolejki działają wg. schematu FIFO (first in first out), elementy pobierane są przezpopz początku kolejki, a nowe dodawane przez pushna jej końcu. Zwykła kolejka w sklepie zachowuje się dokładnie wg. reguły FIFO.

3. Kolejka dwustronna umożliwia dodawanie i pobieranie elementów do/z obu swoich końców. Każda z operacji ma teraz 2 warianty:push_leftipush_rightorazpop_leftipop_right.

4. Kolejka cykliczna lub inaczej bufor cykliczny ma strukturę pętli, jej koniec łączy się z początkiem. Typowe zastosowanie to np. system monitoringu przechowujący tylko określoną liczbę ostatnich ujęć z kamery.

Nowe klatki dodawane są na końcu kolejki, a w przypadku zapełnienia pamięci, te najnowsze zaczynają nadpisywać najstarsze. Operacja popzwraca najstarszy nienadpisany element z bufora, a pushdopisuje nowy w następnym wolnym miejscu lub – jak powiedziałem wyżej – nadpisuje najstarszy element, gdy nie ma już wolnego miejsca. W ten sposób bufor cykliczny nigdy nie przestaje działać na skutek przepełnienia.

5. Kolejka priorytetowa przechowuje elementy danych wraz z określonym priorytetem, przeważnie liczbą całko- witą. Operacjapushdodaje nową parę element-priorytet do kolejki, natomiast operacjapopzwraca element o najwyższym wśród wszystkich priorytecie lub – gdy kilka elementów ma ten sam najwyższy priorytet – najstarszy z nich. Z punktu widzenia użytkownika mechanizm segregowania elementów wg. priorytetu jest niewidoczny.

Zadania polegają na zaimplementowaniu takich struktur używając kolejno: statycznie zadeklarowanych tablic1 (o z góry przyjętym rozmiarze, np. n=10000), list dynamicznych, tablic deklarowanych dynamicznie, które w przypadku zapełnienia będą umiały samoczynnie się powiększyć (lub zmniejszyć jeśli znaczna ich część (np. 75%) pozostaje nieużywana). Dążymy do tego, aby mechanizm implementacji był ukryty przed użytkownikiem przez umieszczenie odp. deklaracji i funkcji obsługujących w plikach nagłówkowych. W ten sposób po przywołaniu tych plików w deklaracji#include ...użytkownik może po prostu korzystać z tych struktur w swoim programie:

int main(){

kolejka X, Y, Z;

...

if ( (!empty(X)) && (peek(X)>0) ) { p = pop(X);

push(p,Y);

1Wiem, że niektórzy z Państwa potrafią już wprawnie posługiwać się dynamiczną alokacją pamięci, ale proszę na obecnym etapie tego nie robić. Zaczynamy od rozwiązańnajprostszych.

(2)

} ...

}

Znając zasadę działania kolejki, możemy swobodnie programować, nie wiedząc jednocześnie nic o sposobie jej implementacji.

Zwróćcie uwagę na sposób operowania stosami, kolejkami przy przekazywaniu ich jako parametrów do funkcjipop, pushitp. Każda z takich struktur – jeśli jest implementowana w tablicy – musi przechowywać gdzieś wskaźniki na element szczytowy (to w przypadku stosu) albo początkowy i końcowy (dla kolejki), tak aby operacjepushi popumiały trafić pod właściwy adres. Chodzi o to, żeby oprócz samego stosu czy kolejkiX nie trzeba było tworzyć osobnych zmiennych jako skojarzonych z Xwskaźników. Jeśli założymy dla uproszczenia, że stosy i kolejki w postaci tablic przechowują jako dane liczby typuint, można wykorzystać standardowo elementX[0]jako wskaźnik na aktualny szczyt stosu (czyli że elementy zajęte w stosie w danej chwili to X[1], X[2],... X[X[0]]) i wtedy operacje pop, push itd. operują odpowiednio wartościąX[0]. Dla kolejki potrzeba dwóch wskaźników: na początek (wykorzystywany przezpop) i na koniec (dla operacji

push), mogą więc nimi być elementy X[0] i X[1], a przestrzeń robocza zaczyna się odX[2]. Jeśli stos lub kolejka przechowują dane innego niż int typu, nadal można użyć elementówX[0], X[1] na przechowanie wskaźników z zastosowaniem jakiejś odpowiedniej konwersji z i na formatint.

Drugi ważny element implementacji to jej odporność na błędy. Przy użyciu statycznie zadeklarowanej tablicy istnieje niebezpieczeństwo jej przepełnienia. Operacjapushpowinna wykrywać takie zagrożenie. Po- dobnie niezabezpieczona operacja popzastosowana do pustego stosu mogłaby naruszyć rezerwację pamięci.

Wszystkie operacje powinny być odporne na takie sytuacje. A więc np. operacjapushna stosie mogłaby być zrealizowana tak:

int push(int x, int S[]){

if (S[0]<n-1){

S[++S[0]]=x;

return 0;

}

return 1; // kod bledu - przepelnienie stosu }

gdzienjest ustalonym rozmiarem tablicy–stosu. Jak widać w awaryjnej sytuacji sygnalizuje ona, że nie można dodać kolejnego elementu i pozostawia stos niezmieniony. Próba pobrania czegoś z pustego stosu,p=pop(X), może zwrócić dowolną nieokreśloną wartość, ale nie powinna zepsuć funkcjonalności tej struktury, to jest nie może zmniejszyć w tej sytuacji X[0]poniżej 0. W istocie dobrym zwyczajem użytkownika powinno być upewnienie się przy pomocy wywołaniaempty(X)przed operacjąpop, że stos nie jest pusty, zawsze gdy nie ma co do tego pewności. Temu właśnie służy funkcjaempty. Jeśli jednak użytkownik zaniedba sprawdzenie i mimo wszystko spróbuje pobrać wartość z pustego stosu, dostanie w odpowiedzi daną “z powietrza”, ale nie może to zdezorganizować działania stosu na przyszłość.

W przypadku kolejki realizowanej w tablicy pojawia się jeszcze jeden problem. Nie jesteśmy w stanie przewidzieć w jakiej kolejności będą wykonywane operacje push i pop. Te ostatnie pobierają elementy z początku kolejki, zwalniając jednocześnie miejsca w tablicy, a push zajmuje kolejne wolne elementy bliżej końca tablicy. W ten sposób zajęty obszar “wędruje” stopniowo w tej tablicy z lewa na prawo. Może się zdarzyć, że kolejna operacjapushosiągnie koniec obszaru tablicy i nie będzie w stanie dopisać tam nowego elementu, podczas gdy z lewej strony kolejki wcześniej zwolniło się sporo miejsca. Funkcja pushpowinna wykrywać i radzić sobie z takimi sytuacjami. Powinna wówczas przesunąć zawartość kolejki w lewo w tablicy by uwolnić maksymalnie dużo miejsca po prawej, aktualizując odpowiednio wskaźnikiX[0]iX[1]. Ponieważ wiąże się to z czasochłonnym przepisaniem na ogół dużej liczby elementów tablicy w inne miejsce, taka operacja “naprawy”

powinna być wykonywana tylko i wyłącznie w stanie konieczności.

(3)

Proszę się zastanowić jak najrozsądniej rozwiązać podobny problem dla kolejki dwustronnej, czyli takiej, która może rosnąć i kurczyć się z obu stron. Zapełnienie wolnej przestrzeni może pojawić w lewym jak i w prawym końcu tablicy przy próbie realizacji push_leftalbopush_right. Podpowiem, że najkorzystniejsza sytuacja jest wtedy, statystycznie rzecz biorąc, gdy kolejka zajmuje obszar jak najbliżej środka tablicy, a wolne miejsce jest podzielone po równo z lewej i z prawej strony. Jeśli operacje pushi pop są wykonywane mniej więcej jednakowo często po lewej i po prawej stronie, ustawienie danych kolejki na środku tablicy najlepiej chroni ją przez szybkim przepełnieniem z jednej ze stron.

Cytaty

Powiązane dokumenty

[r]

Napisz program pobierający od Użytkownika wartość prędkości początkowej oraz kąt w rzucie ukośnym i wyświetlający na ekranie 100 par liczb reprezentujących

Omów pojęcie funkcji w zagadnieniach programistycznych. Podaj sposób implementacji funkcji w języku C++. Omów znaczenie pojęcia deklaracji i definicji funkcji. Wyjaśnij

Napisz program obliczający wartość odchylenia standardowego wartości średniej n liczb wprowadzonych przez Użytkownika. Liczby powinny być zgromadzone

Rozbuduj klasę Zesp z zadania 4a aby posiadała funkcje (metody) Wypisz i Ustaw odpowiednio wypisującą na ekranie daną liczbę zespoloną oraz zmieniającą jej

Omów specyfikatory dostępu private, protected i public i ich wpływ na obiekty dziedziczone.. Zaprezentuj program wykorzystujący

diagramie klas obiektu, którego stany modelujemy Podstawowym zadaniem diagramu stanów jest. uchwycenie możliwych stanów każdego istotnego obiektu systemu, ale można go

• automatyka przemysłowa: PLC Ladder (Siemens, Allen-Bradley, Mitsubishi Electric, Beckhoff) , SCA- DA, DCS −→ Industrial IoT (Python, Java, C/C++ z protokołami komunikacji),