📄 listfunc.c
字号:
/* * Copyright (c) 1993 David I. Bell * Permission is granted to use, distribute, or modify this source, * provided that this copyright notice remains intact. * * List handling routines. * Lists can be composed of any types of values, mixed if desired. * Lists are doubly linked so that elements can be inserted or * deleted efficiently at any point in the list. A pointer is * kept to the most recently indexed element so that sequential * accesses are fast. */#include "value.h"static LISTELEM *elemalloc MATH_PROTO((void));static LISTELEM *listelement MATH_PROTO((LIST *lp, long index));static void elemfree MATH_PROTO((LISTELEM *ep));static void removelistelement MATH_PROTO((LIST *lp, LISTELEM *ep));/* * Free lists for list headers and list elements. */static FREELIST headerfreelist = { sizeof(LIST), /* size of list header */ 20 /* number of free headers to keep */};static FREELIST elementfreelist = { sizeof(LISTELEM), /* size of list element */ 1000 /* number of free list elements to keep */};/* * Insert an element before the first element of a list. */voidinsertlistfirst(lp, vp) LIST *lp; /* list to put element onto */ VALUE *vp; /* value to be inserted */{ LISTELEM *ep; /* list element */ ep = elemalloc(); copyvalue(vp, &ep->e_value); if (lp->l_count == 0) lp->l_last = ep; else { lp->l_cacheindex++; lp->l_first->e_prev = ep; ep->e_next = lp->l_first; } lp->l_first = ep; lp->l_count++;}/* * Insert an element after the last element of a list. */voidinsertlistlast(lp, vp) LIST *lp; /* list to put element onto */ VALUE *vp; /* value to be inserted */{ LISTELEM *ep; /* list element */ ep = elemalloc(); copyvalue(vp, &ep->e_value); if (lp->l_count == 0) lp->l_first = ep; else { lp->l_last->e_next = ep; ep->e_prev = lp->l_last; } lp->l_last = ep; lp->l_count++;}/* * Insert an element into the middle of list at the given index (zero based). * The specified index will select the new element, so existing elements * at or beyond the index will be shifted down one position. It is legal * to specify an index which is right at the end of the list, in which * case the element is appended to the list. */voidinsertlistmiddle(lp, index, vp) LIST *lp; /* list to put element onto */ long index; /* element number to insert in front of */ VALUE *vp; /* value to be inserted */{ LISTELEM *ep; /* list element */ LISTELEM *oldep; /* old list element at desired index */ if (index == 0) { insertlistfirst(lp, vp); return; } if (index == lp->l_count) { insertlistlast(lp, vp); return; } oldep = NULL; if ((index >= 0) && (index < lp->l_count)) oldep = listelement(lp, index); if (oldep == NULL) math_error("Index out of bounds for list insertion"); ep = elemalloc(); copyvalue(vp, &ep->e_value); ep->e_next = oldep; ep->e_prev = oldep->e_prev; ep->e_prev->e_next = ep; oldep->e_prev = ep; lp->l_cache = ep; lp->l_cacheindex = index; lp->l_count++;}/* * Remove the first element from a list, returning its value. * Returns the null value if no more elements exist. */voidremovelistfirst(lp, vp) LIST *lp; /* list to have element removed */ VALUE *vp; /* location of the value */{ if (lp->l_count == 0) { vp->v_type = V_NULL; return; } *vp = lp->l_first->e_value; lp->l_first->e_value.v_type = V_NULL; removelistelement(lp, lp->l_first);}/* * Remove the last element from a list, returning its value. * Returns the null value if no more elements exist. */voidremovelistlast(lp, vp) LIST *lp; /* list to have element removed */ VALUE *vp; /* location of the value */{ if (lp->l_count == 0) { vp->v_type = V_NULL; return; } *vp = lp->l_last->e_value; lp->l_last->e_value.v_type = V_NULL; removelistelement(lp, lp->l_last);}/* * Remove the element with the given index from a list, returning its value. */voidremovelistmiddle(lp, index, vp) LIST *lp; /* list to have element removed */ long index; /* list element to be removed */ VALUE *vp; /* location of the value */{ LISTELEM *ep; /* element being removed */ ep = NULL; if ((index >= 0) && (index < lp->l_count)) ep = listelement(lp, index); if (ep == NULL) math_error("Index out of bounds for list deletion"); *vp = ep->e_value; ep->e_value.v_type = V_NULL; removelistelement(lp, ep);}/* * Remove an arbitrary element from a list. * The value contained in the element is freed. */static voidremovelistelement(lp, ep) register LIST *lp; /* list header */ register LISTELEM *ep; /* list element to remove */{ if ((ep == lp->l_cache) || ((ep != lp->l_first) && (ep != lp->l_last))) lp->l_cache = NULL; if (ep->e_next) ep->e_next->e_prev = ep->e_prev; if (ep->e_prev) ep->e_prev->e_next = ep->e_next; if (ep == lp->l_first) { lp->l_first = ep->e_next; lp->l_cacheindex--; } if (ep == lp->l_last) lp->l_last = ep->e_prev; lp->l_count--; elemfree(ep);}/* * Search a list for the specified value starting at the specified index. * Returns the element number (zero based) of the found value, or -1 if * the value was not found. */longlistsearch(lp, vp, index) LIST *lp; VALUE *vp; long index;{ register LISTELEM *ep; if (index < 0) index = 0; ep = listelement(lp, index); while (ep) { if (!comparevalue(&ep->e_value, vp)) { lp->l_cache = ep; lp->l_cacheindex = index; return index; } ep = ep->e_next; index++; } return -1;}/* * Search a list backwards for the specified value starting at the * specified index. Returns the element number (zero based) of the * found value, or -1 if the value was not found. */longlistrsearch(lp, vp, index) LIST *lp; VALUE *vp; long index;{ register LISTELEM *ep; if (index >= lp->l_count) index = lp->l_count - 1; ep = listelement(lp, index); while (ep) { if (!comparevalue(&ep->e_value, vp)) { lp->l_cache = ep; lp->l_cacheindex = index; return index; } ep = ep->e_prev; index--; } return -1;}/* * Index into a list and return the address for the value corresponding * to that index. Returns NULL if the element does not exist. */VALUE *listfindex(lp, index) LIST *lp; /* list to index into */ long index; /* index of desired element */{ LISTELEM *ep; ep = listelement(lp, index); if (ep == NULL) return NULL; return &ep->e_value;}/* * Return the element at a specified index number of a list. * The list is indexed starting at zero, and negative indices * indicate to index from the end of the list. This routine finds * the element by chaining through the list from the closest one * of the first, last, and cached elements. Returns NULL if the * element does not exist. */static LISTELEM *listelement(lp, index) register LIST *lp; /* list to index into */ long index; /* index of desired element */{ register LISTELEM *ep; /* current list element */ long dist; /* distance to element */ long temp; /* temporary distance */ BOOL forward; /* TRUE if need to walk forwards */ if (index < 0) index += lp->l_count; if ((index < 0) || (index >= lp->l_count)) return NULL; /* * Check quick special cases first. */ if (index == 0) return lp->l_first; if (index == 1) return lp->l_first->e_next; if (index == lp->l_count - 1) return lp->l_last; if ((index == lp->l_cacheindex) && lp->l_cache) return lp->l_cache; /* * Calculate whether it is better to go forwards from * the first element or backwards from the last element. */ forward = ((index * 2) <= lp->l_count); if (forward) { dist = index; ep = lp->l_first; } else { dist = (lp->l_count - 1) - index; ep = lp->l_last; } /* * Now see if we have a cached element and if so, whether or * not the distance from it is better than the above distance. */ if (lp->l_cache) { temp = index - lp->l_cacheindex; if ((temp >= 0) && (temp < dist)) { dist = temp; ep = lp->l_cache; forward = TRUE; } if ((temp < 0) && (-temp < dist)) { dist = -temp; ep = lp->l_cache; forward = FALSE; } } /* * Now walk forwards or backwards from the selected element * until we reach the correct element. Cache the location of * the found element for future use. */ if (forward) { while (dist-- > 0) ep = ep->e_next; } else { while (dist-- > 0) ep = ep->e_prev; } lp->l_cache = ep; lp->l_cacheindex = index; return ep;}/* * Compare two lists to see if they are identical. * Returns TRUE if they are different. */BOOLlistcmp(lp1, lp2) LIST *lp1, *lp2;{ LISTELEM *e1, *e2; long count; if (lp1 == lp2) return FALSE; if (lp1->l_count != lp2->l_count) return TRUE; e1 = lp1->l_first; e2 = lp2->l_first; count = lp1->l_count; while (count-- > 0) { if (comparevalue(&e1->e_value, &e2->e_value)) return TRUE; e1 = e1->e_next; e2 = e2->e_next; } return FALSE;}/* * Copy a list */LIST *listcopy(oldlp) LIST *oldlp;{ LIST *lp; LISTELEM *oldep; lp = listalloc(); oldep = oldlp->l_first; while (oldep) { insertlistlast(lp, &oldep->e_value); oldep = oldep->e_next; } return lp;}/* * Allocate an element for a list. */static LISTELEM *elemalloc(){ LISTELEM *ep; ep = (LISTELEM *) allocitem(&elementfreelist); if (ep == NULL) math_error("Cannot allocate list element"); ep->e_next = NULL; ep->e_prev = NULL; ep->e_value.v_type = V_NULL; return ep;}/* * Free a list element, along with any contained value. */static voidelemfree(ep) LISTELEM *ep;{ if (ep->e_value.v_type != V_NULL) freevalue(&ep->e_value); freeitem(&elementfreelist, (FREEITEM *) ep);}/* * Allocate a new list header. */LIST *listalloc(){ register LIST *lp; lp = (LIST *) allocitem(&headerfreelist); if (lp == NULL) math_error("Cannot allocate list header"); lp->l_first = NULL; lp->l_last = NULL; lp->l_cache = NULL; lp->l_cacheindex = 0; lp->l_count = 0; return lp;}/* * Free a list header, along with all of its list elements. */voidlistfree(lp) register LIST *lp;{ register LISTELEM *ep; while (lp->l_count-- > 0) { ep = lp->l_first; lp->l_first = ep->e_next; elemfree(ep); } freeitem(&headerfreelist, (FREEITEM *) lp);}/* * Print out a list along with the specified number of its elements. * The elements are printed out in shortened form. */voidlistprint(lp, max_print) LIST *lp; long max_print;{ long count; long index; LISTELEM *ep; if (max_print > lp->l_count) max_print = lp->l_count; count = 0; ep = lp->l_first; index = lp->l_count; while (index-- > 0) { if ((ep->e_value.v_type != V_NUM) || (!qiszero(ep->e_value.v_num))) count++; ep = ep->e_next; } if (max_print > 0) math_str("\n"); math_fmt("list (%ld element%s, %ld nonzero)", lp->l_count, ((lp->l_count == 1) ? "" : "s"), count); if (max_print <= 0) return; /* * Walk through the first few list elements, printing their * value in short and unambiguous format. */ math_str(":\n"); ep = lp->l_first; for (index = 0; index < max_print; index++) { math_fmt(" [[%ld]] = ", index); printvalue(&ep->e_value, PRINT_SHORT | PRINT_UNAMBIG); math_str("\n"); ep = ep->e_next; } if (max_print < lp->l_count) math_str(" ...\n");}/* * Return a trivial hash value for a list. */HASHlisthash(lp) LIST *lp;{ HASH hash; hash = lp->l_count * 600011; if (lp->l_count > 0) hash = hash * 600043 + hashvalue(&lp->l_first->e_value); if (lp->l_count > 1) hash = hash * 600053 + hashvalue(&lp->l_last->e_value); return hash;}/* END CODE */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -