Hlavní navigace

GLib: Obousměrné seznamy (2)

Michal Burda

Minule jsme se zakousli do kapitoly o obousměrných seznamech. Naučili jsme se základní operace s těmito datovými typy. Dnes se pustíme do trochu odvážnější manipulace s nimi.

Prohledávání

Budete-li kdy potřebovat v seznamech vyhledávat položky, práci vám usnadní čtveřice funkcí, které jsou pro tyto účely implementovány.

Následující dvě funkce procházejí seznamem list a vracejí gint ovou hodnotu – pořadí, nebo chcete-li index daného prvku. V čem se jejich práce liší, je identifikace „dané“ položky. Zatímco funkce

gint g_list_position(GList *list, GList *llink); 

jako parametr požaduje přímo prvek llink seznamu (jehož index určí), funkci

gint g_list_index(GList *list, gpointer data); 

stačí k nalezení indexu prvku pouze pointer na jeho  data.

Číslování probíhá od nuly a tedy bude-li odpovídajícím prvkem právě první položka seznamu, vrátí se 0. Nepodaří-li se stanovenou položku v seznamu najít, bude návratová hodnota –1.

Trochu z jiného úhlu se na hledání v seznamu dívají následující dvě funkce:

GList* g_list_find(GList *list, gpointer data);
GList* g_list_find_custom(GList *list, gpointer data,
                          GCompareFunc func);

Obě vracejí pointer na nalezenou položku seznamu, obě požadují jako parametr list počátek tohoto seznamu stejně tak jako pointer na data hledaného prvku. Odlišný však je způsob, jakým položku GList vyhledají.

g_list_find() 

Použitím funkce g_list_find_cus­tom() máte nad vyhledáváním úplnou kontrolu. Jako poslední argument této funkce se předává nám již známá funkce typu GCompareFunc a právě ona rozhoduje o relaci mezi dvěma položkami seznamu a tak potažmo i o tom, který prvek seznamu bude prohlášen za ten pravý. Jak se taková funkce vytváří, jsme si popsali minule. V tomto případě jako parametry dostane dva gconstpointer y: data položky seznamu (struktury GList) a uživatelská data (pointer, který je předán funkci g_list_find_cus­tom()). Funkce func pak musí rozhodnout, jsou-li tyto dvě položky shodné a v případě rovnosti vrátit 0. (V případě nerovnosti nenulovou hodnotu.)

Příklad:

/* Priklad pouziti funkce g_list_find_custom() */

#include <glib.h>

gint rovno(gconstpointer a, gconstpointer b)
{
  return (strcmp(a, b));
}

gint main(void)
{
  GList* list = NULL;
  GList* nalezeno;

  list = g_list_append(list, "Josef");
  list = g_list_append(list, "Karel");
  list = g_list_append(list, "Petr");
  list = g_list_append(list, "Hugo");

  nalezeno = g_list_find_custom(list, "Petr", rovno);
  if (nalezeno)
    printf("Nalezeno: %s\n", nalezeno->data);

  g_list_free(list);
}

Foreach

Při práci se seznamy se často opakuje kód, ve kterém provádíme nějakou operaci na každou položku seznamu: třeba nějaké výpočty, tisk a podobně. Knihovna GLib pro tyto účely disponuje funkcí

void g_list_foreach(GList *list, GFunc func, gpointer user_data); 

…která na každý prvek seznamu list zavolá funkci func a mimo jiné jí předá i data  user_data.

A jak se s touto konstrukcí pracuje? Pochopitelně, nejprve si musíme vytvořit funkci, která bude provádět námi požadované operace. Předpis GFunc takové funkce je:

void (*GFunc)(gpointer data, gpointer user_data); 

Tedy je to funkce nevracející nic, která vyžaduje dva argumenty. Prvním z nich, data, je pointer na data položky seznamu. Parametr user_data pak zase uživatelská data, které se předávají funkci g_list_foreach().

Máme-li funkci func definovánu, můžeme zavolat funkci g_list_foreach(). Ta vezme každý prvek obousměrného seznamu list a na jeho data zavolá funkci func, pro kterou jako argumenty použije právě data aktuální položky a parametr  user_data.

Příklad:

/* Ukazka pouziti funkce g_list_foreach() */

#include <glib.h>

/* Funkce odpovidajici typu GFunc - tisk polozky seznamu */
void print_item(gpointer data, gpointer user_data)
{
  printf("%s - jmeno: %s\n", user_data, data);
}

gint main(void)
{
  GList* list = NULL;

  list = g_list_append(list, "Josef");
  list = g_list_append(list, "Karel");
  list = g_list_append(list, "Petr");
  list = g_list_append(list, "Silvestr");
  list = g_list_append(list, "Emil");
  list = g_list_append(list, "Hugo");

  g_list_foreach(list, print_item, "Seznam hracu");

  g_list_free(list);
}

Po spuštění této malé ukázky by se mělo vypsat:

Seznam hracu - jmeno: Josef
Seznam hracu - jmeno: Karel
Seznam hracu - jmeno: Petr
Seznam hracu - jmeno: Silvestr
Seznam hracu - jmeno: Emil
Seznam hracu - jmeno: Hugo

Další funkce

guint g_list_length(GList *list); 

…vrátí počet prvků v obousměrném seznamu list.

GList* g_list_copy(GList *list); 

…vytvoří nově alokovanou kopii seznamu list. Pointery na data se do nového seznamu pouze jednoduše kopírují, takže ukazují-li na nějaké dynamické datové položky, budou mít oba seznamy všechny ukazatele na stejná data (jinými slovy, kopie dynamických dat se neprovádí).

GList* g_list_reverse(GList *list); 

…obrátí pořadí prvků v seznamu list a vrátí pointer na nový počátek. (Tzn. první položka bude poslední, druhá bude předposlední atd.)

GList* g_list_sort(GList *list, GCompareFunc compare_func); 

…seřadí seznam list podle kritéria definovaného funkcí compare_func. Tato funkce musí být typu GCompareFunc a více jste se o ní mohli dočíst v minulém díle. Funkce vrátí pointer na nový začátek seznamu nebo NULL, je-li argument list roven NULL.

GList* g_list_concat(GList *list1, GList *list2); 

…seznam list2 připojí na konec seznamu list1. Pozor: položky druhého seznamu nejsou duplikovány. Prvky seznamu list2 se použijí přímo.

Pokud jste dočetli až sem, víte už o obousměrných seznamech téměř vše. Záměrně jsem však ještě vynechal několik fakt o alokaci položek seznamů do paměti. Jelikož se tento způsob v knihovně GLib používá častěji, nechám si jej někdy na později, až budeme mít více znalostí. Příště se můžete těšit na praktickou ukázku použití obousměrných seznamů: na implementaci mechanizmu automatického doplňování řetězců (automatic string completion).

Našli jste v článku chybu?
Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

Vitalia.cz: Taky věříte na pravidlo 5 sekund?

Taky věříte na pravidlo 5 sekund?

Lupa.cz: Kdo pochopí vtip, může jít do ČT vyvíjet weby

Kdo pochopí vtip, může jít do ČT vyvíjet weby

Vitalia.cz: Znáte „černý detox“? Ani to nezkoušejte

Znáte „černý detox“? Ani to nezkoušejte

DigiZone.cz: Co chtějí operátoři při přechodu na DVB-T2?

Co chtějí operátoři při přechodu na DVB-T2?

Vitalia.cz: „Připluly“ z Německa a možná obsahují jed

„Připluly“ z Německa a možná obsahují jed

Lupa.cz: Co se dá měřit přes Internet věcí

Co se dá měřit přes Internet věcí

Podnikatel.cz: EET: Totálně nezvládli metodologii projektu

EET: Totálně nezvládli metodologii projektu

Měšec.cz: Air Bank zruší TOP3 garanci a zdražuje kurzy

Air Bank zruší TOP3 garanci a zdražuje kurzy

Podnikatel.cz: Snížení DPH na 15 % se netýká všech

Snížení DPH na 15 % se netýká všech

Lupa.cz: Proč firmy málo chrání data? Chovají se logicky

Proč firmy málo chrání data? Chovají se logicky

Podnikatel.cz: Podnikatelům dorazí varování od BSA

Podnikatelům dorazí varování od BSA

Podnikatel.cz: Chaos u EET pokračuje. Jsou tu další návrhy

Chaos u EET pokračuje. Jsou tu další návrhy

Vitalia.cz: I církev dnes vyrábí potraviny

I církev dnes vyrábí potraviny

120na80.cz: Na ucho teplý, nebo studený obklad?

Na ucho teplý, nebo studený obklad?

120na80.cz: Pánové, pečujte o svoje přirození a prostatu

Pánové, pečujte o svoje přirození a prostatu

Vitalia.cz: Mondelez stahuje rizikovou čokoládu Milka

Mondelez stahuje rizikovou čokoládu Milka

Vitalia.cz: Láska na vozíku: Přitažliví jsme pro tzv. pečovatelky

Láska na vozíku: Přitažliví jsme pro tzv. pečovatelky

Vitalia.cz: Jsou čajové sáčky toxické?

Jsou čajové sáčky toxické?

Podnikatel.cz: Chtějte údaje k dani z nemovitostí do mailu

Chtějte údaje k dani z nemovitostí do mailu