linked lists primer

QUESTIONS

  • 1. How are lists handled in DERVISH?
  • 2. What TCL verbs are there to handle Dervish lists?
  • 3. What do I need to do to use these functions?
  • ANSWERS

    1. How are lists handled in DERVISH? Simple tcl lists can be used for plotting coordinates and other simple operations. We use the list utilities in Dervish for lists of more involved objects, such as stars and galaxies. 2. What TCL verbs are there to handle Dervish lists? Basic Functions: listNew create a new list listAdd add an element to a list listRem remove an element from a list listDel delete an entire list Inspecting Lists: iterInit iterNext iterPrev listToHandleArray handleArrayDel listApply listPrint Commands Specific to each list type: fooInit fooNew fooDel For these three commands, "foo" will be replaced by the type of object in the list, for example, "star" or "galaxy". 3. What do I need to do to use these functions? There is an example already built that has a simple "foo" object in lists. The structure looks like this: typedef struct foo { TYPE type; struct foo *next, *prev; char *name; int i; long l; float f; } FOO; Each object on the list has a name and three numbers (an integer, long, and float.) You will also need a procedure called fooInit which initializes a list of foos. This is included in file $INT_DIR/lists/fooInit.tcl: # # Initialize a FOO # proc fooInit {foo name i l f } { foreach el "name i l f" { memberSet $foo $el [set $el] } return $foo } First you need to build the dervish program that has foo lists installed: sdsshost % cp $INT_DIR/lists/fooInit.tcl . sdsshost % cp $DERVISH_DIR/examples/Makefile . sdsshost % cp $DERVISH_DIR/examples/foo.c . sdsshost % cp $DERVISH_DIR/examples/foo.h . sdsshost % cp $DERVISH_DIR/examples/dervish_foo.c . sdsshost % cp $DERVISH_DIR/examples/tclFoo.c . sdsshost % sdssmake dervish_foo Notice that it created new source code files called diskio_gen.c and diskio_gen.h, along with an executable called dervish_foo. Now run the program and build some lists: sdsshost % dervish_foo Executing commands in /usr/products/IRIX/dervish/v3_1/etc/dervishStartup.tcl: foo> source fooInit.tcl foo> fooInit [fooNew] "First Object" 1 2.3 4.5 h0 foo> structPrint h0 type FOO type (enum)1009 next 0x0 prev 0x0 name First Object i 1 l 2 f 4.5 The first four lines of this structure printout are for maintaining the linked list. The last four lines are what we set for this object. Notice that the value of l is truncated from 2.3 to 2, since it is an integer, but that the value 4.5 is retained in a float. Now we can use TCL to fill up a short list: foo> listNew FOO h1 foo> foreach n {"Object One" "Object Two" "Object Three" } { >> set foo [fooInit [fooNew] $n 0 1 2.3] >> listAdd h1 $foo >> handleDel $foo >> } And we can look at what this gives by: foo> listPrint h1 type FOO type (enum)1009 next 0x100b65b0 prev 0x0 name Object One i 0 l 1 f 2.3 type FOO type (enum)1009 next 0x100b6dc4 prev 0x100b5dc8 name Object Two i 0 l 1 f 2.3 type FOO type (enum)1009 next 0x0 prev 0x100b65b0 name Object Three i 0 l 1 f 2.3 To delete the second item from this list: foo> iterInit h1 h3 This initializes the handle h3 and has it point to the first one on the list. foo> iterNext h3 h3 This has the handle h3 point to the next member of the list. foo> listRem h1 h3 This actually removes the element of the list. foo> listPrint h1 type FOO type (enum)1009 next 0x100b6dc4 prev 0x0 name Object One i 0 l 1 f 2.3 type FOO type (enum)1009 next 0x0 prev 0x100b5dc8 name Object Three i 0 l 1 f 2.3 foo> ------------------------------------------------------------ Appendix: Steve Kent's notes on lists LINKED LISTS in DERVISH - Steve Kent June 23, 1993 [WARNING: I make no guarantees for the accuracy of the information herein]. [I assume that you have DERVISH installed on your system and that if you type "setup dervish", something useful will happen]. ******************************************************************************* I. Introduction A common function in image processing is to create and store lists of things such as objects in an image. Typically one might store such lists in arrays that are pre-dimensioned to some large value. Since one cannot predict in advance how many objects will be found, if the dimensions are picked badly, one either wastes space or ends up with lists that are too big for the arrays. Linked lists provide an alternative mechanism to arrays for storing these lists. The idea is that one allocates storage for parameters for a single object at a time and then uses pointers to connect the objects together. Although such linked lists are a bit more complicated to use than arrays, they are more flexible and efficient of storage and can be arbitrarily long. NOTE: Linked lists are not the same thing at all as TCL lists. In this document, the term list always refers to a linked list unless otherwise specified. A linked list starts with three members: An integer that gives the type of things on the list, a pointer to the first item in the list, and a pointer to the last element. When a list is first created, all pointers are NULL. An element is a structure whose first member is also an integer that gives the type and whose next two members are pointers that serve to link the list. The first pointer points to the next element in the list; the second pointer points to the previous element in the list. The first element in the list has its prev pointer set to NULL. The last element in the list has its next pointer set to NULL. The remaining members of each element are the data items that you are interested in storing. Schematically, here is a linked list of 3 objects where each object has a row, column, and counts field: --------- | Type | --------- ---------------------------- | First | | --------- | | Last | ------------------------------- | --------- | | | | | | Object 1 Object 2 Object 3 | | | | --------- --------- --------- | ---> | Type | <----- ----> | Type | <----- ----> | Type | <------ --------- | | --------- | | --------- | Next | -----|--- | Next | -----|--- | NULL | --------- | --------- | --------- | NULL | -------- | Prev | -------- | Prev | --------- --------- --------- | Row | | Row | | Row | --------- --------- --------- | Col | | Col | | Col | --------- --------- --------- | Cnts | | Cnts | | Cnts | --------- --------- --------- The type field everywhere is a value that indicates that this list contains OBJECTs. Dervish provides both C and TCL level functions to create lists, add and remove elements from lists, and traverse lists. There are also TCL only routines to print out the contents of lists or their elements. WARNING: The TCL routines are not yet very robust. It is possible to do apparently sensible things (which aren't) that cause the program to core dump. ******************************************************************************* II. CREATING NEW LIST TYPES I will assume that you know how to compile and link a new version of dervish using the template Makefile and dervish_template.c file that are found in $DERVISH_DIR/example. Each list has a type associated with it (e.g., one could have OBJECTS, STARS, STREAKS, whatever). Dervish does not have ANY built-in types. For each type of list that you want, you need to write some supporting C code. Here is a list of the things that need to be created for each new type of list: 1) A .h include file that defines a structure for an element in the list. 2) A .c file that has two routines: one to create a new element (e.g., by using shMalloc) and one to delete an element (e.g., by using shFree). 3) A .c file that creates two new Tcl commands to interface to the create and delete routines. In addition, you need to modify the following files: 4) $DERVISH_DIR/include/types.h to add a new definition of your list. 5) $DERVISH_DIR/examples/Makefile to add your new .c files to the make process. 6) $DERVISH_DIR/examples/dervish_foo.c to add code to define your new TCL commands The $DERVISH_DIR/examples has a set of files to illustrate the above process. They contain code to create a structure called FOO that can be linked into lists. The FOO structure is not very interesting - it contains just 3 numbers. Here a sample of how to create a version of dervish that has FOO objects in it. 1. Create a new subdirectory and cd there. 2. setup dervish (hopefully this gives you v3_1 or later of dervish - if not, get it or forget the rest of this document). 3. cp $DERVISH_DIR/examples/foo.c . cp $DERVISH_DIR/examples/foo.h . cp $DERVISH_DIR/examples/tclFoo.c . cp $DERVISH_DIR/examples/dervish_foo.c . cp $DERVISH_DIR/examples/Makefile . 4. Now type sdssmake dervish_foo This should compile a new version of dervish called dervish_foo. Along the way, it creates new source files called diskio_gen.c and diskio_gen.h that will sit in your current directory. ******************************************************************************* III. Using Lists A. TCL Level You can now run the program dervish_foo and try out the TCL level list manipulation functions. These are documented more fully in the help file tclLists.html located in the $DERVISH_DIR/doc/www area. Here is a list of all the TCL level commands and their arguments: listNew listAdd [where] listDelIfEmpty listDel * listRem iterInit * iterNext * iterPrev * listToHandleArray * handleArrayDel * listApply * listPrint NOTE: commands marked with (*) are implemented as tcl procedures and are defined in the dervish startup file. Normally, this file is loaded when you first run dervish provided you did "setup dervish" first. If you forgot to do so, then these commands will not exist. In addition, you have created two new commands: fooNew fooDel CAUTION: At the tcl level, handles are used to refer to both lists and elements of lists. [If you don't know what handles are, they are symbolic names that are used to refer to C structures and so forth. When you type fooNew for example, it prints out the name of the handle that refers to the FOO element that you have just created]. Handles are also used to refer to other things that are not lists or capable of being on lists (regions, for example). Not all of the TCL list code is yet capable of checking to see that a handle refers to a valid thing for the type of operation that it is about to carry out. B. C Level Much of the time you will want to used linked lists at the C level. Here is a list of the C commands that let you use lists: Descriptions of C functions shListNew Create a new list shListDel Delete an existin list shListAdd Add an element to a list shListRem Remove an element from a list The definitions of these functions and how to use them are given in the document $DERVISH_DIR/doc/www/lists.html. There are some additional functions defined there that I did not list above. Note that there are no equivalents to the TCL commands that let you initialize and travserse a list. You are expected to do so explictly by using the pointers in the linked list structures. Actually, it would not be difficult to create generic C routines that peform those functions. Perhaps someone wants to do so? ******************************************************************************* IV. More on creating Linked Lists The above foo example was rather easy to implement and did not give you a chance to see all that is involved in making new list types. Here I will explain how to make a new list type called OBJECT that looks like the example given in section I. The steps are as follows: 1. Make copies of the files foo.c, foo.h, and tclFoo.c and call them object.c, object.h, and tclObject.c. 2. Copy the file $DERVISH_DIR/include/types.h to your local working directory. 3. Edit the files and change all the foo references to object. 4. Edit object.h, so that the OBJECT typedef looks like the following: typedef struct object { TYPE type; struct object *next, *prev; float row; /* New line */ float col; /* New line */ float cnts; /* New line */ } OBJECT; 5. Edit object.c so that the procedure shObjectNew looks as follows: OBJECT * shObjectNew(void) { OBJECT *new = shMalloc(sizeof(OBJECT)); new->type = TYPE_OBJECT; new->next = new->prev = NULL; new->row = new->col = new->cnts = 0.; /* New line */ return(new); } 6. Edit the files tclFoo.c and tclObject.c. Near the top of the file where the various include statements are listed, insert the following line #include "types.h" immediately after the line #include "tcl.h". This causes these routines to use the local copy of the header file types.h rather than the one stored in $SHIV_DIR/include. 7. Edit the file types.h. Add an extra item at the end of the TYPE definition to define TYPE_OBJECT. Thus, the definition in this file looks like typedef enum { TYPE_UNKNOWN = 1000, TYPE_INT, TYPE_LONG, TYPE_FLOAT, TYPE_STR, TYPE_ENUM, TYPE_PTR, TYPE_THING, TYPE_LIST, TYPE_FOO, TYPE_MASK, TYPE_REGION, TYPE_REGINFO, TYPE_NTYPE, /* Add a comma here */ TYPE_OBJECT /* New line */ } TYPE; 8. Edit the Makefile. On the line that begins FOO_OBJS, add two new modules. This line now reads... FOO_OBJS = dervish_foo.o foo.o tclFoo.o diskio_gen.o object.o tclObject.o In the definition of DISKIO_FILES, add object.h to the end of the line. These lines now read DISKIO_FILES = $(DERVISH_DIR)/include/types.h \ $(DERVISH_DIR)/include/lists.h \ $(DERVISH_DIR)/include/region.h foo.h object.h 9. Edit the file dervish_foo.c. Add an extra line to declare the new object tcl commands as follows: ftclCreateCommands(handle.interp); /* Fermi enhancements to TCL */ ftclCreateHelp (handle.interp); /* Define help command */ ftclMsgDefine (handle.interp); /* Define help for tcl verbs */ ftclParseInit(handle.interp); /* Ftcl command parsing */ shTclFooDeclare(handle.interp); shTclObjectDeclare(handle.interp); /* NEW LINE!!!! */ 10. Now you are ready to remake the executable. Type sdssmake dervish_foo If all is well, you will get a new version of the program of dervish_foo that include the new object definitions. NOTE: The Makefile has two ways of making executable programs. One uses dervish_template.c as its main and the other uses dervish_foo.c as its main. The latter is derived from dervish_template.c (with a few transparent modifications). These two ways should probably be merged. Good luck and have fun!