222 lines
7.9 KiB
C
222 lines
7.9 KiB
C
/* ******************************* c203.c *********************************** */
|
|
/* Předmět: Algoritmy (IAL) - FIT VUT v Brně */
|
|
/* Úkol: c203 - Fronta znaků v poli */
|
|
/* Referenční implementace: Petr Přikryl, 1994 */
|
|
/* Přepis do jazyka C: Václav Topinka, září 2005 */
|
|
/* Úpravy: Kamil Jeřábek, září 2020 */
|
|
/* Daniel Dolejška, září 2021 */
|
|
/* Daniel Dolejška, září 2022 */
|
|
/* ************************************************************************** */
|
|
/*
|
|
** Implementujte frontu znaků v poli. Přesnou definici typů naleznete
|
|
** v hlavičkovém souboru c203.h (ADT fronta je reprezentována strukturou Queue,
|
|
** která obsahuje pole 'array' pro uložení hodnot ve frontě a indexy firstIndex
|
|
** a freeIndex. Všechny implementované funkce musí předpokládat velikost pole
|
|
** QUEUE_SIZE, i když ve skutečnosti jsou rozměry statického pole definovány
|
|
** MAX_QUEUE. Hodnota QUEUE_SIZE slouží k simulaci fronty v různě velkém poli
|
|
** a nastavuje se v testovacím skriptu c203-test.c před testováním
|
|
** implementovaných funkcí. Hodnota QUEUE_SIZE může nabývat hodnot v rozsahu
|
|
** 1 až MAX_QUEUE.
|
|
**
|
|
** Index firstIndex ukazuje vždy na první prvek ve frontě. Index freeIndex
|
|
** ukazuje na první volný prvek ve frontě. Pokud je fronta prázdná, ukazují
|
|
** oba indexy na stejné místo. Po inicializaci ukazují na první prvek pole,
|
|
** obsahují tedy hodnotu 0. Z uvedených pravidel vyplývá, že v poli je vždy
|
|
** minimálně jeden prvek nevyužitý.
|
|
**
|
|
** Při libovolné operaci se žádný z indexů (firstIndex i freeIndex) nesnižuje
|
|
** vyjma případu, kdy index přesáhne hranici QUEUE_SIZE. V tom případě
|
|
** se "posunuje" znovu na začátek pole. Za tímto účelem budete deklarovat
|
|
** pomocnou funkci NextIndex, která pro kruhový pohyb přes indexy pole
|
|
** využívá operaci "modulo".
|
|
**
|
|
** Implementujte následující funkce:
|
|
**
|
|
** Queue_Init ...... inicializace fronty
|
|
** nextIndex ....... pomocná funkce - viz popis výše
|
|
** Queue_IsEmpty ... test na prázdnost fronty
|
|
** Queue_IsFull .... test, zda je fronta zaplněna (vyčerpána kapacita)
|
|
** Queue_Front ..... přečte hodnoty prvního prvku z fronty
|
|
** Queue_Remove .... odstraní první prvek fronty
|
|
** Queue_Dequeue ... přečte a odstraní první prvek fronty
|
|
** Queue_Enqueue ... zařazení prvku na konec fronty
|
|
**
|
|
** Nemusíte ošetřovat situaci, kdy místo legálního ukazatele na seznam
|
|
** předá někdo jako parametr hodnotu NULL.
|
|
**
|
|
** Své řešení účelně komentujte!
|
|
**
|
|
**/
|
|
|
|
#include "c203.h"
|
|
|
|
int QUEUE_SIZE = MAX_QUEUE;
|
|
bool error_flag;
|
|
bool solved;
|
|
|
|
/**
|
|
* Vytiskne upozornění na to, že došlo k chybě.
|
|
* Tato funkce bude volána z některých dále implementovaných operací.
|
|
*
|
|
* TUTO FUNKCI, PROSÍME, NEUPRAVUJTE!
|
|
*
|
|
* @param error_code Interní identifikátor chyby
|
|
*/
|
|
void Queue_Error( int error_code ) {
|
|
static const char *QERR_STRINGS[MAX_QERR + 1] = {
|
|
"Unknown error",
|
|
"Queue error: ENQUEUE",
|
|
"Queue error: FRONT",
|
|
"Queue error: REMOVE",
|
|
"Queue error: DEQUEUE",
|
|
"Queue error: INIT"
|
|
};
|
|
|
|
if (error_code <= 0 || error_code > MAX_QERR)
|
|
{
|
|
error_code = 0;
|
|
}
|
|
printf("%s\n", QERR_STRINGS[error_code]);
|
|
error_flag = 1;
|
|
}
|
|
|
|
/**
|
|
* Inicializujte frontu následujícím způsobem:
|
|
* - všechny hodnoty v poli queue->array nastavte na '*',
|
|
* - index na začátek fronty nastavte na 0,
|
|
* - index prvního volného místa nastavte také na 0.
|
|
*
|
|
* V případě, že funkce dostane jako parametr queue == NULL, volejte funkci
|
|
* Queue_Error(QERR_INIT).
|
|
*
|
|
* @param queue Ukazatel na strukturu fronty
|
|
*/
|
|
void Queue_Init( Queue *queue ) {
|
|
if (queue == NULL)
|
|
{
|
|
Queue_Error(QERR_INIT);
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < QUEUE_SIZE; i++)
|
|
{
|
|
queue->array[i] = '*';
|
|
}
|
|
queue->firstIndex = 0;
|
|
queue->freeIndex = 0;
|
|
}
|
|
|
|
/**
|
|
* Pomocná funkce, která vrací index následujícího prvku v poli.
|
|
* Funkci implementujte jako jediný prikaz využívající operace '%'.
|
|
* Funkci nextIndex budete využívat v dalších implementovaných funkcích.
|
|
*
|
|
* @param index Aktuální index
|
|
*/
|
|
int nextIndex( int index ) {
|
|
return (index + 1) % QUEUE_SIZE;
|
|
}
|
|
|
|
/**
|
|
* Vrací nenulovou hodnotu, pokud je frona prázdná, jinak vrací hodnotu 0.
|
|
* Funkci je vhodné implementovat jedním příkazem return.
|
|
*
|
|
* @param queue Ukazatel na inicializovanou strukturu fronty
|
|
*/
|
|
int Queue_IsEmpty( const Queue *queue ) {
|
|
return (queue->firstIndex == queue->freeIndex);
|
|
}
|
|
|
|
/**
|
|
* Vrací nenulovou hodnotu, je-li fronta plná, jinak vrací hodnotu 0.
|
|
* Funkci je vhodné implementovat jedním příkazem return
|
|
* s využitím pomocné funkce nextIndex.
|
|
*
|
|
* @param queue Ukazatel na inicializovanou strukturu fronty
|
|
*/
|
|
int Queue_IsFull( const Queue *queue ) {
|
|
return (nextIndex(queue->freeIndex) == queue->firstIndex);
|
|
}
|
|
|
|
/**
|
|
* Prostřednictvím parametru dataPtr vrátí znak ze začátku fronty queue.
|
|
* Pokud je fronta prázdná, ošetřete to voláním funkce Queue_Error(QERR_FRONT).
|
|
* Volání této funkce při prázdné frontě je vždy nutné považovat za nekorektní.
|
|
* Bývá to totiž důsledek špatného návrhu algoritmu, ve kterém je fronta
|
|
* použita. O takové situaci se proto musí programátor-vývojář dozvědět.
|
|
* V opačném případě je ladění programů obtížnější!
|
|
*
|
|
* Při implementaci využijte dříve definované funkce Queue_IsEmpty.
|
|
*
|
|
* @param queue Ukazatel na inicializovanou strukturu fronty
|
|
* @param dataPtr Ukazatel na cílovou proměnnou
|
|
*/
|
|
void Queue_Front( const Queue *queue, char *dataPtr ) {
|
|
if (Queue_IsEmpty(queue))
|
|
{
|
|
Queue_Error(QERR_FRONT);
|
|
return;
|
|
}
|
|
*dataPtr = queue->array[queue->firstIndex];
|
|
}
|
|
|
|
/**
|
|
* Odstraní znak ze začátku fronty queue. Pokud je fronta prázdná, ošetřete
|
|
* vzniklou chybu voláním funkce Queue_Error(QERR_REMOVE).
|
|
* Hodnotu na uvolněné pozici ve frontě nijak neošetřujte (nepřepisujte).
|
|
* Při implementaci využijte dříve definované funkce Queue_IsEmpty a nextIndex.
|
|
*
|
|
* @param queue Ukazatel na inicializovanou strukturu fronty
|
|
*/
|
|
void Queue_Remove( Queue *queue ) {
|
|
if (Queue_IsEmpty(queue))
|
|
{
|
|
Queue_Error(QERR_REMOVE);
|
|
return;
|
|
}
|
|
queue->firstIndex = nextIndex(queue->firstIndex);
|
|
}
|
|
|
|
/**
|
|
* Odstraní znak ze začátku fronty a vrátí ho prostřednictvím parametru dataPtr.
|
|
* Pokud je fronta prázdná, ošetřete to voláním funkce Queue_Error(QERR_DEQUEUE).
|
|
*
|
|
* Při implementaci využijte dříve definovaných funkcí Queue_IsEmpty,
|
|
* Queue_Front a Queue_Remove.
|
|
*
|
|
* @param queue Ukazatel na inicializovanou strukturu fronty
|
|
* @param dataPtr Ukazatel na cílovou proměnnou
|
|
*/
|
|
void Queue_Dequeue( Queue *queue, char *dataPtr ) {
|
|
if (Queue_IsEmpty(queue))
|
|
{
|
|
Queue_Error(QERR_DEQUEUE);
|
|
return;
|
|
}
|
|
Queue_Front(queue, dataPtr);
|
|
Queue_Remove(queue);
|
|
}
|
|
|
|
/**
|
|
* Vloží znak data do fronty. Pokud je fronta plná, ošetřete chybu voláním
|
|
* funkce Queue_Error(QERR_ENQUEUE). Vkládání do plné fronty se považuje za
|
|
* nekorektní operaci. Situace by mohla být řešena i tak, že by operace
|
|
* neprováděla nic, ale v případě použití takto definované abstrakce by se
|
|
* obtížně odhalovaly chyby v algoritmech, které by abstrakci využívaly.
|
|
*
|
|
* Při implementaci využijte dříve definovaných funkcí Queue_IsFull a nextIndex.
|
|
*
|
|
* @param queue Ukazatel na inicializovanou strukturu fronty
|
|
* @param data Znak k vložení
|
|
*/
|
|
void Queue_Enqueue( Queue *queue, char data ) {
|
|
if (Queue_IsFull(queue))
|
|
{
|
|
Queue_Error(QERR_ENQUEUE);
|
|
return;
|
|
}
|
|
queue->array[queue->freeIndex] = data;
|
|
queue->freeIndex = nextIndex(queue->freeIndex);
|
|
}
|
|
|
|
/* Konec příkladu c203.c */
|