The SAS/IML language supports lists, which are containers that store other objects, such as matrices and other lists. A primary use of lists is to pack objects of various types into a single symbol that can be passed to and from modules. A useful feature of using lists is that lists can dynamically grow or shrink as necessary. You can use the ListAddItem subroutine to add a new item to an existing list, which can be convenient if you do not know until run time how many items the list needs to hold.
A SAS/IML programmer asked whether it is possible to add items to a sublist of a list. The answer is yes, and there is even an option to make the process efficient. Intuitively, you need to copy the sublist, modify the list, and then replace the sublist in the list. Because copying a list can be an expensive operation, the SAS/IML language provides a special option to move the list items into new symbols without copying. The move operator assigns the memory to a new symbol and "nulls out" the previous symbol.
Suppose you have a SAS/IML list, L, that contains sublists. Suppose you want to add a new item to the first sublist (L$1). You can do the following:
- Get the sublist by using the ListGetSubItem function. For example, the syntax Y = ListGetSubItem(L, 1, 'm'); gets the first sublist and assigns it to the Y symbol. Using 'm' for the third argument means "move" the memory to the new symbol. After this call, L$1 is empty and the symbol Y points to the sublist that was formerly contained in L$1.
- The symbol Y is a list, so use ListAddItem to add a new item to Y.
- Now set the first item of L. You can use call ListSetSubItem(L, 1, Y, 'm'); to assign L$1 to be the value of Y. Because you use the 'm' option, the memory is moved, not copied. After this call, Y is an empty symbol. The first item of L contains the list that was formerly in Y.
The schematic diagram above illustrates the process. The following SAS/IML program shows how to implement the technique:
proc iml; /* create list of lists. Specify L$1$1, L$1$2, L$2$1, and L$2$2 */ L = ListCreate(2); L$1 = ['L11', 12]; /* first item of L is a list with two items */ L$2 = ['L21', 22]; /* second item of L is a list with two items */ /* Now add a third item to L$1 and access it as L$1$3 */ newVal = 23; Y = ListGetSubItem(L, 1, 'm'); /* get the sublist */ call ListAddItem(Y, newVal); /* add new item */ call ListSetSubItem(L, 1, Y, 'm'); /* set it back in same position */ /* check that L$1$3 exists and that S is empty */ package load ListUtil; call struct(L); L13 = L$1$3; dimY = dimension(Y); print L13, dimY; |
Summary
A useful feature of SAS/IML lists is that a list can dynamically grow. Lists also include an option that moves (rather than copies) memory to and from list items. You can use this feature to efficiently add items to a sublist of a list. For clarity, I used three separate function calls in the example program. If you plan to use this feature repeatedly, you can encapsulate the operations into a SAS/IML user-defined function.
1 Comment
Thanks for writing this Rick. You might also mention that this approach is equally good for sub-sublists, or even deeper, since the ListGetSubItem and ListSetSubItem subroutines both accept a matrix as the 'position' parameter giving the item number at each depth.