Previous Page | Next Page

SCL Lists

Implementing SCL Sublists and Nested Structures

SCL allows you to put one list of items inside another SCL list, thereby making a sublist. For example, you can read the columns of a SAS table row into a list. You could then insert each row into another list, and repeat this process for a range of rows in the SAS table. You then have a list of lists, where the "outer" list contains an element for each row in the SAS table, and the "inner" sublists contain each row. These lists are called nested lists.

To illustrate, consider the SAS table WORK.EMPLOYEES, created with the following DATA step program:

data employees;
   input fname $ 1-9 lname $ 10-18
         position $ 19-28 salary 29-34;
 datalines;
 Walter   Bluerock Developer 36000
 Jennifer Godfrey  Manager   42000
 Kevin    Blake    Janitor   19000
 Ronald   Tweety   Publicist 29000
 ;

The following example reads the WORK.EMPLOYEES table into an SCL list. The outer list is the list in the variable OUTERLIST. Each time through the loop, a new inner list is created. Its identifier is stored in the variable INNERLIST, and INNERLIST is inserted at the end of OUTERLIST.

INIT:
      /* Open the EMPLOYEES table and   */
      /* create the SCL list OUTERLIST  */
   dsid=open('employees');
   outerList=makelist();
      /* Read the first table row and    */
      /* find the number of its columns  */
   rc=fetch(dsid);
   numcols=attrn(dsid,'NVARS');
       /* For each row, make a new INNERLIST  */
       /* and create and insert the sublists  */
   do while (rc=0);
      innerList=makelist();
         /* For each column, return the name  */
         /* and type.  Insert a list item of  */
         /* that name and type into the       */
         /* row's INNERLIST.                  */
      do i=1 to numcols;
         name=varname(dsid,i);
         type=vartype(dsid,i);
         if type='N' then
            rc=insertn(innerList,(getvarn
                       (dsid,i)),-1,name);
         else
            rc=insertc(innerList,(getvarc
                       (dsid,i)),-1,name);
      end;
         /* Insert each INNERLIST as an item  */
         /* into OUTERLIST and read the next  */
         /* row of the EMPLOYEES table        */
      outerList=insertl(outerList,innerList,-1);
      rc=fetch(dsid);
   end;
      /* Close the EMPLOYEES table.  Print and  */
      /* then delete OUTERLIST and its sublists. */
   sysrc=close(dsid);
   call putlist(outerList,'Nested Lists',2);
   rc=dellist(outerList,'y');
   return;

This program produces the following output:

Nested Lists( ( FNAME='Walter'
                LNAME='Bluerock'
                POSITION='Developer'
                SALARY=36000
               )[7]   1 
              ( FNAME='Jennifer'
                LNAME='Godfrey'
                POSITION='Manager'
                SALARY=42000
               )[9]   1 
              ( FNAME='Kevin'
                LNAME='Blake'
                POSITION='Janitor'
                SALARY=19000
               )[11]  1 
              ( FNAME='Ronald'
                LNAME='Tweety'
                POSITION='Publicist'
                SALARY=29000
               )[13]  1 
              )[5] 1     2 

  1. [5], [7], [9], [11], and [13] are the list identifiers that were assigned when this example was run. These values may be different each time the example runs.

  2. List identifier 5 identifies the "outer" list. Each row is an inner or nested list (list identifiers 7, 9, 11, and 13).


Limitless Levels of Nesting

Nested lists are highly useful for creating collections of records or data structures. There is no limit to the amount of nesting or to the number of sublists that can be placed in a list, other than the amount of memory available to your SAS application. Further, you can create recursive list structures, where the list A can contain other lists that contain A either directly or indirectly. The list A can even contain itself as a list item.


Simulating Multidimensional Arrays with Nested Lists

You can declare multidimensional arrays in SCL, but all lists are one-dimensional. That is, to access an item in a list, you specify only one index. However, you can use nested lists to simulate multidimensional arrays. For example, to create a list structure that mimics a 2 by 3 array, you can use the following example:

array a[2,3] 8 _temporary_;
init:
   listid = makelist(2);
   lista = setiteml(listid, makelist(3), 1);
   listb = setiteml(listid, makelist(3), 2);
   call putlist(listid);
   do i = 1 to dim(a,1);
      list=getiteml(listid,i);
      do j = 1 to dim(a,2);
         a[i, j] = 10*i + j;
         put a[i,j]=;
         rc = setitemn(list,a[i,j], j);
      end;
   end;
   call putlist(listid);
return;

This example produces the following output:

((. . . )[7] (. . . )[9] )[5]
a[ 1 , 1 ]=11
a[ 1 , 2 ]=12
a[ 1 , 3 ]=13
a[ 2 , 1 ]=21
a[ 2 , 2 ]=22
a[ 2 , 3 ]=23
((11 12 13 )[7] (21 22 23 )[9] )[5]

Note:   Not all of the program is shown here. You would need to delete these lists before ending the program. [7], [9], and [5] are the list identifiers that were assigned when this example was run and may be different each time the example is run.  [cautionend]


Saving Nested Lists to SCL Entries

When you save a list that contains sublists, both the list and its sublists are saved in the same SLIST entry. Thus, if you create list data structures that are highly recursive and have many cycles, you should be careful about saving your lists.

For example, suppose list A contains list B. When you save list A, you also save list B; you do not need to save list B separately, because list B is already stored in list A. In fact, if you store the lists in two separate SLIST entries and then try to read them back, you do not get the same list structure that you stored originally.

The following example creates two lists, A and B, (with text values in them to identify their contents) and inserts list B into list A. It then saves each list in separate SLIST entries, A.SLIST and B.SLIST. Then, the program creates two more lists, APRIME and BPRIME, reads the two saved SLIST entries into those two lists, and then prints all the list identifiers and list values.

INIT:
      /* Make lists A and B and insert an item */
      /* of text into each list.  Then, insert */
      /* list B into list A.                   */
   a = makelist();
   a = insertc(a, 'This is list A');
   b = makelist();
   b = insertc(b, 'This is list B');
   a = insertl(a, b);
      /* Save lists A and B into separate  */
      /* SLIST entries.                    */
   rc=savelist
      ('CATALOG','SASUSER.LISTS.A.SLIST', A);
   rc=savelist
      ('CATALOG','SASUSER.LISTS.B.SLIST', B);

      /* Make lists APRIME and BPRIME.  Fill  */
      /* APRIME with the contents of A.SLIST  */
      /* and BPRIME with B.SLIST              */
   aPrime=makelist();
   bPrime=makelist();
   rc=fillist
      ('CATALOG','SASUSER.LISTS.A.SLIST', aPrime);
   rc=fillist
      ('CATALOG','SASUSER.LISTS.B.SLIST', bPrime);
      /* Store list APRIME into list BINA */
   bInA = getiteml(aPrime);
   put a= b= aPrime= bPrime= bInA= ;
   call putlist(a, 'List A:',0);
   call putlist(b, 'List B:',0);
   call putlist(aPrime, "List aPrime:",0);
   call putlist(bPrime, "List bPrime:",0);
      /* Delete list A and its sublist B  */
      /* Delete lists APRIME, BPRIME, and BINA */
   rc=dellist(a,'y');
   rc=dellist(aPrime);
   rc=dellist(bPrime);
return;

Here is the output:

a=5 b=7 aPrime=9 bPrime=11 bIna=13
List A:(('This is list B
         )[7]
        'This is list A
        )[5]
List B:('This is list B
        )[7]
List aPrime:(('This is list B
          )[13]
         'This is list A
         )[9]
List bPrime:('This is list B
         )[11]

Note that the sublist B (13) that was read from A.SLIST is not the same as the sublist BPRIME (11) that was read from B.SLIST. That is, A contains B, but B does not contain BPRIME. Therefore, changes made to B are inherently reflected in A, whereas changes to BPRIME are not reflected in APRIME.

Also note that the structures of list A and list APRIME are the same, but the list identifiers are different and do not match any of the list identifiers that were read from B.SLIST.

Note:   [5], [7], [9], [11], and [13] are the list identifiers that were assigned when this example was run and may be different each time the example runs.  [cautionend]


Advantages of SAVELIST Recursiveness

There is an advantage to the recursive nature of the SAVELIST function. For example, if list A contains sublists B and C, SAVELIST saves all three lists when you save A to an SLIST entry. Your application can take advantage of this if you have several unrelated lists that you want to save. By creating a new list and inserting the lists that you want saved into the new list, you can save them all in one SLIST entry with one SAVELIST call, instead of saving each sublist in a separate SLIST entry with separate SAVELIST calls.

Previous Page | Next Page | Top of Page