inweb-bootstrap/foundation-module/Chapter_2/Linked_Lists_and_Stacks.nw

261 lines
8 KiB
Text

[LinkedLists::] Linked Lists and Stacks.
A simple implementation for single-linked lists of objects allocated by Foundation's
memory manager, and for last-in-first-out stacks of same.
@ \section{Implementation.}
Basically, there's a head structure, which points to a chain of body structures,
each linking to the next. But to reduce memory manager overhead, we're going to store
the first few body structures inside the head structure: that way, for a list of just
a few items, only one call to the memory manager is needed.
<<*>>=
#define NO_LL_EARLY_ITEMS 32
<<*>>=
typedef struct linked_list {
struct linked_list_item *first_list_item;
struct linked_list_item *last_list_item;
int linked_list_length;
int early_items_used;
struct linked_list_item early_items[NO_LL_EARLY_ITEMS];
CLASS_DEFINITION
} linked_list;
typedef struct linked_list_item {
void *item_contents;
struct linked_list_item *next_list_item;
} linked_list_item;
<<*>>=
linked_list *LinkedLists::new(void) {
linked_list *ll = CREATE(linked_list);
LinkedLists::empty(ll);
return ll;
}
void LinkedLists::empty(linked_list *ll) {
ll->linked_list_length = 0;
ll->early_items_used = 0;
ll->first_list_item = NULL;
ll->last_list_item = NULL;
}
@ The following runs in constant time, i.e., performs no loops. In general we
want speed rather than memory efficiency.
<<*>>=
void LinkedLists::add(linked_list *L, void *P, int to_end) {
if (L == NULL) internal_error("null list");
linked_list_item *item = NULL;
if (L->early_items_used < NO_LL_EARLY_ITEMS)
item = &(L->early_items[L->early_items_used++]);
else
item = CREATE(linked_list_item);
item->item_contents = P;
if (to_end) {
item->next_list_item = NULL;
if (L->last_list_item == NULL) {
L->first_list_item = item;
L->last_list_item = item;
} else {
L->last_list_item->next_list_item = item;
L->last_list_item = item;
}
} else {
item->next_list_item = L->first_list_item;
L->first_list_item = item;
if (L->last_list_item == NULL) L->last_list_item = item;
}
L->linked_list_length++;
}
@ Because of the direction of the links, only removing from the front is quick:
<<*>>=
void *LinkedLists::remove_from_front(linked_list *L) {
if (L == NULL) internal_error("null list");
linked_list_item *front = L->first_list_item;
if (front == NULL) internal_error("empty list can't have item 0 removed");
L->first_list_item = front->next_list_item;
if (L->first_list_item == NULL) L->last_list_item = NULL;
L->linked_list_length--;
return front->item_contents;
}
@ It's rather slower to delete from a known position in the middle:
<<*>>=
void *LinkedLists::delete(int N, linked_list *L) {
if (L == NULL) internal_error("null list");
if ((N < 0) [[]] (N >= L->linked_list_length)) internal_error("index not valid");
if (N == 0) return LinkedLists::remove_from_front(L);
for (linked_list_item *item = L->first_list_item; item; item = item->next_list_item) {
N--;
if (N == 0) {
if (L->last_list_item == item->next_list_item) L->last_list_item = item;
void *contents_deleted = item->next_list_item->item_contents;
item->next_list_item = item->next_list_item->next_list_item;
L->linked_list_length--;
return contents_deleted;
}
}
internal_error("index not found");
return NULL;
}
@ And indeed to insert at a known position, where [[N]] being 0 means the front
of the list, [[N]] being 1 means "after the first item", and so on.
<<*>>=
void LinkedLists::insert(linked_list *L, int N, void *P) {
if (N <= 0) LinkedLists::add(L, P, FALSE);
else {
linked_list_item *prev = NULL;
for (linked_list_item *I = L->first_list_item; I; I = I->next_list_item) {
if (N-- == 0) break;
prev = I;
}
if (prev == NULL) LinkedLists::add(L, P, FALSE);
else {
linked_list_item *item = NULL;
if (L->early_items_used < NO_LL_EARLY_ITEMS)
item = &(L->early_items[L->early_items_used++]);
else
item = CREATE(linked_list_item);
item->item_contents = P;
linked_list_item *subs = prev->next_list_item;
prev->next_list_item = item;
item->next_list_item = subs;
L->linked_list_length++;
}
}
}
@ \section{A function call API.}
<<*>>=
int LinkedLists::len(linked_list *L) {
return L?(L->linked_list_length):0;
}
linked_list_item *LinkedLists::first(linked_list *L) {
return L?(L->first_list_item):NULL;
}
void *LinkedLists::entry(int N, linked_list *L) {
if ((N < 0) [[| (L == NULL) |]] (N >= L->linked_list_length)) return NULL;
for (linked_list_item *I = L->first_list_item; I; I = I->next_list_item)
if (N-- == 0)
return I->item_contents;
return NULL;
}
void LinkedLists::set_entry(int N, linked_list *L, void *P) {
if ((N < 0) [[| (L == NULL) |]] (N >= L->linked_list_length)) return;
for (linked_list_item *I = L->first_list_item; I; I = I->next_list_item)
if (N-- == 0) {
I->item_contents = P; return;
}
}
linked_list_item *LinkedLists::last(linked_list *L) {
return L?(L->last_list_item):NULL;
}
linked_list_item *LinkedLists::next(linked_list_item *I) {
return I?(I->next_list_item):NULL;
}
void *LinkedLists::content(linked_list_item *I) {
return I?(I->item_contents):NULL;
}
@ \section{A macro-ized API.}
These intentionally hide the implementation. The difference between
[[FIRST_IN_LINKED_LIST]] and [[FIRST_ITEM_IN_LINKED_LIST]] is that one returns
the first structure in the list, and the other returns the first
[[linked_list_item]] chunk in the list. From the latter you can make the
former using [[CONTENT_IN_ITEM]], but not vice versa. The same object
may be listed in many different lists, so if all you have is the object,
you don't know its place in the list.
<<*>>=
#define NEW_LINKED_LIST(T)
(LinkedLists::new())
#define FIRST_ITEM_IN_LINKED_LIST(T, L)
(LinkedLists::first(L))
#define ENTRY_IN_LINKED_LIST(N, T, L)
((T *) (LinkedLists::entry(N, L)))
#define DELETE_FROM_LINKED_LIST(N, T, L)
((T *) (LinkedLists::delete(N, L)))
#define LAST_ITEM_IN_LINKED_LIST(T, L)
(LinkedLists::last(L))
#define NEXT_ITEM_IN_LINKED_LIST(I, T)
(LinkedLists::next(I))
#define CONTENT_IN_ITEM(I, T)
((T *) (LinkedLists::content(I)))
#define ADD_TO_LINKED_LIST(I, T, L)
LinkedLists::add(L, (void *) (I), TRUE)
#define FIRST_IN_LINKED_LIST(T, L)
((T *) (LinkedLists::content(LinkedLists::first(L))))
#define LAST_IN_LINKED_LIST(T, L)
((T *) (LinkedLists::content(LinkedLists::last(L))))
@ The following macro requires slight care to use: the list [[L]] needs to be
calculable without side-effects. There's no such worry over [[P]] or [[T]], since
they're just identifier names: the loop variable and the type name respectively.
Note that the loop variable [[P]] must already be defined. Inside the loop body,
a new variable will also then exist, [[P_item]], to refer to the item which
points to [[P]]. This allows us to iterate despite the comments above.
<<*>>=
#define LOOP_OVER_LINKED_LIST(P, T, L)
for (linked_list_item *P##_item = (P = FIRST_IN_LINKED_LIST(T, L), FIRST_ITEM_IN_LINKED_LIST(T, L));
P##_item;
P##_item = (P = CONTENT_IN_ITEM(NEXT_ITEM_IN_LINKED_LIST(P##_item, T), T), NEXT_ITEM_IN_LINKED_LIST(P##_item, T)))
@ \section{LIFO stacks.}
The above gives us an almost free implementation of LIFO, last-in-first-out,
stacks, where we represent a stack as a linked list whose first entry is at
the front. To push an item, we add it at the front; to pull, we remove the
front item.
We provide an abstract type name for these stacks, even though they're the
exact same structure. For reasons to do with the way [[typedef]] works in C,
it is awkward to typedef the two names together, so we'll simply use the
preprocessor:
<<*>>=
#define lifo_stack linked_list
@ Otherwise, it's macros all the way:
<<*>>=
#define NEW_LIFO_STACK(T)
(LinkedLists::new())
#define PUSH_TO_LIFO_STACK(I, T, L)
LinkedLists::add((L), (void *) (I), FALSE)
#define PULL_FROM_LIFO_STACK(T, L)
((T *) LinkedLists::remove_from_front(L))
#define POP_LIFO_STACK(T, L)
(LinkedLists::remove_from_front(L))
#define TOP_OF_LIFO_STACK(T, L)
FIRST_IN_LINKED_LIST(T, L)
#define LIFO_STACK_EMPTY(T, L)
((LinkedLists::len(L) == 0)?TRUE:FALSE)
#define LOOP_DOWN_LIFO_STACK(P, T, L)
LOOP_OVER_LINKED_LIST(P, T, L)