Hlavní navigace

GLib: Klíčované seznamy dat (2)

3. 11. 2000
Doba čtení: 5 minut

Sdílet

Dokončení výkladu o klíčovaných seznamech dat.

Než se pustíme do dalšího výkladu, připomenu ještě úmluvu, že všechny parametry uchovávající zpracovávaný klíčovaný seznam vyžadují pointer na pointer na daný seznam.

Odstraňování prvků z klíčovaného seznamu dat

#define g_datalist_id_remove_data(dl, q) 

…odstraní (a podle potřeby i dealokuje) z klíčovaného seznamu dl (pointer na pointer) prvek s klíčem odpovídajícím kvarku  q.

#define g_datalist_remove_data(dl, k) 

…rovněž odstraňuje z datalistu dl položku s daty, ale místo kvarku jako klíč požaduje řetězec (který se vnitřně na kvark převede).

Pokud položka s klíčem q (popř. k) v datalistu dl neexistuje, nestane se nic.

Potřebujete-li z klíčovaného seznamu odstranit prvek tak, aby se jeho data nedealokovala (ačkoliv v normálním případě by se tak stalo), vyberte si k zavolání následující funkci nebo makro:

void g_datalist_id_remove_no_notify(GData **datalist,
                                    GQuark key_id);

#define g_datalist_remove_no_notify(dl, k)

Funkce g_datalist_id_remove_no_notify() odstraní (avšak nedealokuje) z klíčovaného seznamu datalist prvek s klíčem odpovídajícím kvarku key_id a makro g_datalist_remove_no_notify() udělá totéž se seznamem dl, ale prvek rozezná podle řetězcového klíče  k.

Chcete-li najednou zničit všechny položky klíčovaného seznamu, zavolejte na něj funkci

void g_datalist_clear(GData **datalist); 

Tímto se z klíčovaného seznamu všechna data dealokují i odstraní a datalist bude znovu čistý jako děťátko (a navíc nastaven na  NULL).

Konstrukce „foreach“

Chcete-li snad nějakou rutinu vykonat na všech prvcích klíčovaného seznamu, můžete tak učinit voláním funkce

void g_datalist_foreach(GData **datalist,
                        GDataForeachFunc func,
                        gpointer user_data);

void (*GDataForeachFunc) (GQuark key_id,
                          gpointer data,
                          gpointer user_data);

Funkce g_datalist_foreach() zavolá na každý prvek klíčovaného seznamu datalist uživatelskou funkci func, které předá kvark key_id identifikující daný prvek, jeho data data a další uživatelská data user_data. Uživatelská funkce musí odpovídat prototypu  GDataForeachFunc.

Příklad:

K pochopení tohoto příkladu je nutné být obeznámen s jednosměrnými seznamy.

/* Jednoducha databaze telefonnich cisel */
#include <glib.h>
/* makro pro pridani retezcovych dat do GData */
#define my_set_string_data(dl, k, s)
  g_datalist_set_data_full((dl), (k), (gpointer) g_strdup(s), g_free);


/* pridani zamestnance */
void my_append_work(GSList **seznam, gchar *jmeno,
                    gchar *funkce, gchar *teldomu,
                    gchar *telprace, gfloat plat)
{
  GData *polozka;
  gfloat *pplat;

  g_datalist_init(&polozka);

  g_datalist_set_data(&polozka, "typ", GINT_TO_POINTER(1));

  my_set_string_data(&polozka, "jmeno", jmeno);
  my_set_string_data(&polozka, "funkce", funkce);
  my_set_string_data(&polozka, "teldomu", teldomu);
  my_set_string_data(&polozka, "telprace", telprace);

  pplat = g_malloc(sizeof(gfloat));
  *pplat = plat;
  g_datalist_set_data_full(&polozka, "plat", (gpointer) pplat,
    g_free);

  *seznam = g_slist_append(*seznam, (gpointer) polozka);
}


/* pridani znameho */
void my_append_personal(GSList **seznam, gchar *jmeno,
                        gchar *adresa, gchar *telefon)
{
  GData *polozka;

  g_datalist_init(&polozka);

  g_datalist_set_data(&polozka, "typ", GINT_TO_POINTER(2));

  my_set_string_data(&polozka, "jmeno", jmeno);
  my_set_string_data(&polozka, "adresa", adresa);
  my_set_string_data(&polozka, "telefon", telefon);

  *seznam = g_slist_append(*seznam, (gpointer) polozka);
}


/* funkce pro tisk polozky seznamu */
void my_print_slist(gpointer data, gpointer user_data)
{
  GData *polozka;
  gfloat *plat;

  polozka = (GData *) data;

  if (GPOINTER_TO_INT(g_datalist_get_data(&polozka, "typ")) == 1) {

    /* tiskne se pracovnik */
    plat = g_datalist_get_data(&polozka, "plat");

    printf("Pracovnik: %s, %s; tel: %s, domu: %s; plat: %0.2f Kc
",
      g_datalist_get_data(&polozka, "jmeno"),
      g_datalist_get_data(&polozka, "funkce"),
      g_datalist_get_data(&polozka, "telprace"),
      g_datalist_get_data(&polozka, "teldomu"),
      *plat);

  } else {

    /* tiskne se znamy */
    printf("Znamy:     %s; %s; tel: %s
",
      g_datalist_get_data(&polozka, "jmeno"),
      g_datalist_get_data(&polozka, "adresa"),
      g_datalist_get_data(&polozka, "telefon"));

  }
}


/* dealokace dat */
void my_free_item(gpointer data, gpointer user_data)
{
  GData *polozka;

  polozka = (GData *) data;
  g_datalist_clear(&polozka);
}


/* hlavni telo programu */
gint main(void)
{
  GSList *seznam = NULL;

  /* inicializace seznamu */
  my_append_work(&seznam, "Josef Novak", "manazer",
                 "02/123456", "11", 20000);
  my_append_personal(&seznam, "Karel Kvetinac",
                     "Polni 32, Praha", "02/12126385");
  my_append_personal(&seznam, "Petr Novy",
                     "Akatova 1, Liberec", "026/55443");
  my_append_personal(&seznam, "Pavel Stary",
                     "Hlavni 13, Brno", "03/229933");
  my_append_work(&seznam, "Tomas Lenochod", "delnik",
                 "nema", "21", 8000);
  my_append_personal(&seznam, "Hana Fialova",
                     "Vodni 13/a, Ostrava", "0609/113114");

  /* ... */

  printf("V databazi je celkem %d zaznamu:
",
    g_slist_length(seznam));

  /* tisk seznamu */
  g_slist_foreach(seznam, my_print_slist, NULL);

  /* dealokace */
  g_slist_foreach(seznam, my_free_item, NULL);
  g_slist_free(seznam);
}

Uvedený příklad implementuje jednoduchý telefonní seznam, který je tvořen jednosměrným seznamem GSList. Do GSListseznam jsou za sebou ukládány „nafukovací“ datové záznamy  GData.

Příklad je dlouhý, ale ne složitý. Na začátku main() se seznam kontaktů inicializuje. Použil jsem pro tyto účely dvě speciální funkce my_append_work() a my_append_per­sonal(), které vytvoří jeden záznam a ten uloží do seznam u.

Pro tisk seznamu jsem použil konstrukce „foreach“ GSList ů. Na každý prvek seznamu se zavolá funkce my_print_slist(), která se postará o vytisknutí jednoho záznamu na obrazovku.

Nakonec se celý seznam dealokuje. Před uvolněním samotného GSList u se ale musí dealokovat jeho data (záznamy GData). Pro tyto účely jsem opět využil konstrukce „foreach“, která tentokrát volá funkci my_free_item() a ta uvolní paměť zabíranou prvky jednoho záznamu.

Funkce my_append_work() vytvoří z předaných parametrů záznam o osobě typu „pracovník“ a ten uloží do seznam u. K vytvoření klíčovaného seznamu polozka se využívá pomocné makro my_set_string_data(). Jeho účelem je vytvořit kopii předaného řetězce a tu uložit do záznamu. Všimněte si, že jako „dealokovací“ funkce je použito  g_free.

Funkce my_append_per­sonal() slouží k vytvoření záznamu o „známé“ osobě (ne pracovník). Všimněte si, že jejich uchovávaná data jsou dramaticky různá od „pracovníků“.

Funkce pro tisk kontaktu, my_print_slist(), nejprve podle prvku "typ" záznamu polozka určí, o jaký typ dat jde („pracovník“ nebo „známý“) a podle toho vytiskne příslušné informace na obrazovku.

Funkce my_free_item() sloužící k dealokování dat seznamu jednoduše na každou GData položku seznamu seznam volá g_datalist_clean(), která se postará o správnou dealokaci.

Uvedený příklad prosím chápejte pouze jako ilustraci použití klíčovaných seznamů a ne jako optimální řešení daného problému.

Byl pro vás článek přínosný?

Autor článku

Michal Burda vystudoval informatiku a aplikovanou matematiku a nyní pracuje na Ostravské univerzitě jako odborný asistent. Zajímá se o data mining, Javu a Linux.