#include <stdio.h>
#include <stdlib.h>
#define DICT_MAX_SIZE 100
/*
* Definiujemy strukturę o nazwie `dictionary_element`, która zawiera dwa pola:
* `key` - klucz, po którym będziemy szukali wpisów w słowniku
* `value` - wartość, kóra jest przyporządkowana danemu kluczowi
*/
typedef struct {
int key;
float value;
} dictionary_element;
/*
* Deklaracja to sam nagłówek funkcji zakończony średnikiem, tzn `typ_zwracany nazwa(lista argumentów);`
*
* Podział na deklaracje i definicje to podział na to CO dany program robi i JAK to robi.
* Ten podział ułatwia czytanie i pisanie większych projektów - najpierw ustalamy interfejs,
* czyli CO jakaś część programu robi, a dopiero później piszemy JAK to robi.
*
* Dużą zaletą tego rozwiązania jest wymuszenie na programistach, żeby polegali jedynie na tym CO dana funkcja robi.
* Przykładowo używamy `scanf` i `printf`, a nie musimy wiedzieć, JAK one działają. Co więcej, ich implamentacja może
* się zmieniać (np w zależności od systemu operacyjnego), a dla nas nie będzie to miało znaczenia. Tutaj jest podobnie,
* tzn używając słownika nie musimy martwić się o to jak on działa, wystarczy, że znamy funkcje do jego obsługi.
*/
// Deklaracje funkcji do obsługi słownika
// Inicjalizuje nowy słownik
void dict_init(dictionary_element dict[], int *dict_fill);
// Dodaje element `elem` do słownika, jeśli element już istnieje, to wypisuje błąd
void dict_add(dictionary_element dict[], int *dict_fill, dictionary_element elem);
// Sprawdza, czy dany klucz `key` jest w słowniku, jeśli tak, to zwraca 1, w przeciwnym wypadku zwraca 0
int dict_contains(dictionary_element dict[], int *dict_fill, int key);
// Zwraca wartość przyporządkowaną danemu kluczowi `key`, lub 0.0, jeśli klucza nie ma w słowniku
float dict_get(dictionary_element dict[], int *dict_fill, int key);
// Usuwa wpis o danym kluczu `key` ze słownika
void dict_remove(dictionary_element dict[], int *dict_fill, int key);
// Definicje funkcji do obsługi słownika
void dict_init(dictionary_element dict[], int *dict_fill) {
*dict_fill = 0; // ustawiamy wypełnienie słownika na 0, nie ma potrzeby czyścić pamięci używanej przez `dict`
}
void dict_add(dictionary_element dict[], int *dict_fill, dictionary_element elem) {
if (dict_contains(dict, dict_fill, elem.key)) { // jeśli element już znajduje się w słowniku, to wypisujemy błąd
printf("ERROR: dictionary already contains key: %d\n", elem.key);
return;
}
dict[*dict_fill] = elem; // dodajemy nowy element do słownika na pierwszym wolnym miejscu
(*dict_fill)++; // zwiększamy licznik wypełnienia słownika
printf("SUCCESS: added %d -> %f to dictionary\n", elem.key, elem.value);
}
int dict_contains(dictionary_element dict[], int *dict_fill, int key) {
for (int i = 0; i < *dict_fill; i++) { // musimy przeszukać całą tablicę w posukiwaniu elementu o danym kluczu
if (dict[i].key == key) { // jeśli znaleźliśmy element o danym kluczu, to od razu zwracamy 1 - nie musimy szukać dalej
return 1;
}
}
return 0; // jeśli przeszukaliśmy całą tablicę i nie znaleźliśmy w niej danego klucza, to zwracamy 0
}
float dict_get(dictionary_element dict[], int *dict_fill, int key) {
// Kod jest niemal identyczny do `dict_contains` z tą różnicą, że zwracamy znalezioną wartość
// lub 0.0 jeśli danego klucza nie było w słowniku
for (int i = 0; i < *dict_fill; i++) {
if (dict[i].key == key) {
return dict[i].value;
}
}
printf("ERROR: dictionary does not contain key: %d\n", key);
return 0.0;
}
void dict_remove(dictionary_element dict[], int *dict_fill, int key) {
int index = -1; // ustawiamy wartość zmiennej `index` na -1 - żaden element w tablicy nie ma takiego indeksu
for (int i = 0; i < *dict_fill; i++) {
if (dict[i].key == key) {
index = i;
break; // jeśli znaleźliśmy dany klucz, to wychodzimy z pętli `for`
}
}
if (index == -1) { // jeśli wartość zmiennej `index` to nadal -1, to znaczy, że nie znaleźliśmy danego klucza w słowniku
printf("ERROR: dictionary does not contain key: %d\n", key);
return;
}
printf("SUCCESS: removed %d -> %f from dictionary\n", dict[index].key, dict[index].value);
dict[index] = dict[*dict_fill]; // przesuwamy element z końca tablicy na miejsce usuwanego elementu
(*dict_fill)--; // usuwamy ostatni element tablicy
}
int main() {
dictionary_element dict[DICT_MAX_SIZE];
int dict_fill;
dict_init(dict, &dict_fill);
dictionary_element elem1 = {.key = 5, .value = 6.5};
dictionary_element elem2 = {.key = 123456789, .value = 1.23};
dictionary_element elem3 = {.key = -13, .value = 1.0};
dictionary_element elem4 = {.key = -13, .value = 2.0};
dict_add(dict, &dict_fill, elem1); // dodajemy pierwszy element
dict_add(dict, &dict_fill, elem2); // dodajemy drugi element
dict_add(dict, &dict_fill, elem3); // dodajemy trzeci element
dict_add(dict, &dict_fill, elem4); // tutaj próbujemy dodać element z kluczem, który już jest w słowniku - dostaniemy błąd
// wartość przyporządkowana kluczowi `-13` to nadal `1.0`
printf("Dictionary element: %d -> %f\n", -13, dict_get(dict, &dict_fill, -13));
// w słowniku mamy klucz `5`
printf("Dictionary element: %d -> %f\n", 5, dict_get(dict, &dict_fill, 5));
// usuwamy klucz `5`
dict_remove(dict, &dict_fill, 5);
// teraz w słowniku nie ma już klucza `5` - przy próbie znalezienia go dostaniemy błąd
printf("Dictionary element: %d -> %f\n", 5, dict_get(dict, &dict_fill, 5));
return 0;
}