list.c

Denna kod är public domain. Om ni hittar fel eller vill ändra något i koden blir jag jätteglad om ni skickar dessa ändringar till jesper [at] fantasi [punkt] se.


#include<stdio.h>
#include<stdlib.h>

/**
 * Ett element i listan. Har en referens till data och en till nästa
 * element i listan.
 */
typedef struct Element
{
   void* data;
   struct Element* next;
} Element;

/**
 * Datastrukturen för listan. Håller ordning på listans längd och
 * huvudet för själva listan.
 */
typedef struct List
{
   int length;
   Element* list;
} List;

/**
 * En iterator över listan. Utöver referensen till det aktuella
 * elementet behöver vi även hålla ordning på det tidigare elementet
 * för att kunna radera element.
 */
typedef struct Iterator
{
   Element** prev;
   Element** curr;
} Iterator;


/**
 * Skapa en ny lista.
 */
List* listNew(void)
{
   return (List*)calloc(1, sizeof(List));
}

/**
 * Lägg in ett element först i listan.
 */
void listAdd(List* list, void* data)
{
   Element* elem = malloc(sizeof(Element));

   if (elem != NULL)
   {
      list->length++;
      elem->data = data;
      elem->next = list->list;
      list->list = elem;
   }
}

/**
 * Radera ett element i listan givet en referens till elementet.
 * Returnerar det borttagna elementet eller NULL om inget hittades.
 */
void* listRemoveElem(List* list, void* elem)
{
   Element* curr = list->list;
   Element** prev = &(list->list);

   while (curr != NULL && curr->data != elem)
   {
      prev = &(curr->next);
      curr = curr->next;
   }

   if (curr != NULL)
   {
      void* data = curr->data;
      *prev = curr->next;
      free(curr);
      return data;
   }

   return NULL;
}

/**
 * Radera ett element i listan.
 * comp  - En funktion som tar två referenser till datat som ska jämföras.
 *         Objekten som ska jämföras tas in via void-pekare eftersom listan
 *         är generell och kan hålla vilken typ av data som hellst.
 * value - En referens till ett objekt med samma värde enligt den givna
 *         komparatorn.
 * Returnerar det borttagna elementet eller NULL om inget hittades.
 * Se exemplet nedan.
 */
void* listRemove(List* list, void* value, int comp(void* a, void* b))
{
   Element* curr = list->list;
   Element** prev = &(list->list);

   while (curr != NULL && comp(curr->data, value) != 0)
   {
      prev = &(curr->next);
      curr = curr->next;
   }

   if (curr != NULL)
   {
      void* data = curr->data;
      *prev = curr->next;
      free(curr);
      return data;
   }

   return NULL;
}

/**
 * Returnerar längden på listan.
 */
int listLength(List* list)
{
   return list->length;
}

/**
 * Skapar en iterator över listan. Se exemplet nedan för att se hur
 * man använder en iterator.
 */
void listIteratorInit(Iterator* iter, List* list)
{
   iter->prev = iter->curr = &(list->list);
}

/**
 * Returnerar nästa element i listan givet en iterator.
 */
void* getNext(Iterator* iter)
{
   void* data;

   if (*(iter->curr) == NULL)
      return NULL;

   data = (*(iter->curr))->data;
   iter->prev = iter->curr;
   iter->curr = &((*(iter->curr))->next);
   return data;
}

/**
 * Raderar det senaste elementet som returnerades av getNext.
 */
void removeCurrent(Iterator* iter)
{
   Element* elem = *(iter->prev);

   *(iter->prev) = *(iter->curr);
   iter->curr = iter->prev;

   elem->data = NULL;
   elem->next = NULL;
   free(elem);
}



/**************************************************************/
/** Exempel                                                  **/
/**************************************************************/

/**
 * En funktion som jämför två heltal. Talen som jämförs tas in via
 * argumenten. Notera att funktionen tar pekare som argument. Man
 * måste alltså skicka in adresserna till de heltalsvariabler man vill
 * jämföra.
 */
int compareInt(void *a, void *b)
{
   return *(int*)a - *(int*)b;
}

/**
 * Exemplet skapar en lista med 25 heltal. Sedan använder den de olika
 * metodrna för att plocka bort element ur listan. De 25 heltalen
 * lagras här i en array. Det är endast av bekvämlighetsskäl och har
 * ingen betydelse för listan. Det vanliga är annars att man har en
 * något större datastruktur som man vill lagra i listan och man
 * lägger då in data i listan allt eftersom man skapar dessa
 * datastrukturer.
 */
int main(void)
{
   List* list = listNew();
   Iterator iter;
   int a[25];
   int i;
   int *el;

   /* Skapa listan. Notera att vi skickar in adressen till varje heltal
    * konverterad till en void-pekare.
    */
   for (i = 0; i < 25; i++)
   {
      a[i] = i;
      listAdd(list, (void*)&a[i]);
   }

   /* Skapa en iterator och gå igenom listan. Radera var femte element
    * med hjälp av iteratorns remove-funktion.
    */
   listIteratorInit(&iter, list);
   while ((el = (int*)getNext(&iter)))
   {
      printf("[Loop 1] Hittade: %d\n", *el);
      if (*el % 5 == 0)
      {
         removeCurrent(&iter);
         printf("[Loop 1] Tog bort: %d\n", *el);
      }
   }

   /* Radera elementen 24, 21, 1 och 0 med funktionen som tar en
    * direkt adress till elementet som ska raderas.
    */
   (void)listRemoveElem(list, &a[24]);
   (void)listRemoveElem(list, &a[21]);
   (void)listRemoveElem(list, &a[1]);
   (void)listRemoveElem(list, &a[0]);

   /* Skapa en ny iterator och radera alla element som är större än tio.
    */
   listIteratorInit(&iter, list);
   while ((el = (int*)getNext(&iter)))
   {
      printf("[Loop 2] Hittade: %d\n", *el);
      if (*el > 10)
      {
         removeCurrent(&iter);
         printf("[Loop 2] Tog bort: %d\n", *el);
      }
   }

   /* Radera elementen 4, 6 och 7 med hjälp av funktionen som jämför
    * två heltal. Vi skickar alltså inte in någon adress till det som ska
    * raderas, bara ett annat heltal med samma värde.
    */
   for (i = 4; i < 8; i++)
   {
      if ((el = listRemove(list, &i, &compareInt)) != NULL)
         printf("[Loop 3] Tog bort: %d\n", *el);
   }

   /* Radera alla element som finns kvar.
    */
   listIteratorInit(&iter, list);
   while ((el = (int*)getNext(&iter)))
   {
      printf("[Loop 4] Hittade: %d\n", *el);
      removeCurrent(&iter);
      printf("[Loop 4] Tog bort: %d\n", *el);
   }

   /* Skapa en ny iterator och försök att gå igenom listan. Listan är
    * dock tom så ingen utskrift ska ske.
    */
   listIteratorInit(&iter, list);
   while ((el = (int*)getNext(&iter)))
   {
      printf("[Loop 5] Listan skulle vara tom, hittade: %d\n", *el);
   }

   return EXIT_SUCCESS;
}