/* StackAndQueue.sas Macros to implement stacks and queues using the SAS hash object */ /* Larry Hoyle - October 2008 */ /* ------------ */ /* Stack Macros */ /* ------------ */ %macro StackDefine(stackName = Stack1, /* Name of the stack - */ /* use the name in push and pop of this stack */ dataType = n, /* Datatype n - numeric */ /* c - character */ dataLength = 8, /* Length of data iitems */ hashexp = 8, /* Hashexp for the hash - see the documentation for the */ /* hash object. You may want to increase this for */ /* a stack that will get really large */ rc = Stack1_rc /* return code for stack operations */ ); /* the macro will create the following data objects and variables */ /* &StackName._Hash, the hash object used for the stack */ /* &StackName._key, the hash object used for the stack */ /* &StackName._data, the hash object used for the stack */ /* &StackName._end, variable to hold the number of objects */ /* in the stack */ retain &StackName._end 0; /* empty stack has 0 items */ length &StackName._key 8; /* key is numeric count of items in the stack */ call missing(&StackName._key); /* explicit assignment so SAS does not complain */ %IF &dataType EQ n %THEN %DO; length &StackName._data &datalength; retain &StackName._data 0; %END; %ELSE %DO; length &StackName._data $ &datalength; retain &StackName._data ' '; %END; declare hash &StackName._Hash(hashexp: &hashexp); &rc = &StackName._Hash.defineKey("&StackName._key"); &rc = &StackName._Hash.defineData("&StackName._data"); &rc = &StackName._Hash.defineDone(); /* ITEM_SIZE attribute available in SAS 9.2 itemSize = &StackName._Hash.ITEM_SIZE; */ itemSize = 8 + &datalength; put "Stack &StackName. Created. Each Item will take " ItemSize " bytes."; %mend StackDefine; %macro StackPush(stackName = Stack1, /* Name of the stack - */ InputData = Stack1_data, /* Variable containing value to be pushed onto the stack */ StackLength = Stack1_length, /* Returns the length of the stack */ rc = Stack1_rc /* return code for stack operations */ ); &StackName._end+1 ; /* new item will go in new location in the hash */ &StackLength = &StackName._end; &StackName._key = &StackName._end; /* new value goes at the end */ &StackName._data = &InputData; /* value from &InputData */ &rc = &StackName._Hash.add(); if &rc ne 0 then put "NOTE: PUSH to stack &stackName failed " &InputData= &StackName._end= ; %mend StackPush; %macro StackPop(stackName = Stack1, /* Name of the stack - */ OutputData = Stack1_data, /* Variable containing value to be popped from the stack */ StackLength = Stack1_length, /* Returns the length of the stack */ rc = Stack1_rc /* return code for stack operations */ ); if &StackName._end > 0 then do; &StackName._key = &StackName._end; /* return value comes off of the end */ &rc = &StackName._Hash.find(); if &rc ne 0 then put "NOTE: POP from stack &stackName could not find " &StackName._end= ; &OutputData = &StackName._data; /* value into &InputData */ /* remove the item from the hash */ &rc = &StackName._Hash.remove(); if &rc ne 0 then put "NOTE: POP from stack &stackName could not remove " &StackName._end= ; &StackName._end = &StackName._end - 1 ; /* stack now has 1 fewer item */ &StackLength = &StackName._end; end; else do; &rc = 999999; put "NOTE: Cannot pop empty stack &StackName into &OutputData "; end; %mend StackPop; %macro StackLength(stackName = Stack1, /* Name of the stack - */ StackLength = Stack1_length, /* Returns the length of the stack */ rc = Stack1_rc /* return code for stack operations */ ); &StackLength = &StackName._end; %mend StackLength; %macro StackDelete(stackName = Stack1, /* Name of the stack - */ rc = Stack1_rc /* return code for stack operations */ ); &rc = &StackName._Hash.delete(); if &rc ne 0 then put "NOTE: Cannot delete stack &StackName "; %mend StackDelete; %macro StackDump(stackName = Stack1, /* Name of the stack - */ rc = Stack1_rc /* return code for stack operations */ ); if &StackName._end <= 0 then do; put // "Stack &Stackname is empty"; end; /* &StackName._end <= 0 */ else do; put // " Contents of Stack &Stackname:"; do ixStack = 1 to &StackName._end ; &StackName._key = ixStack; &rc = &StackName._Hash.find(); put "item " ixStack "value " &StackName._data; end; /* do ixStack = 1 to &StackName._end */ end; /* not &StackName._end <= 0 */ %mend StackDump; /* ------------ */ /* Queue Macros */ /* ------------ */ %macro QueueDefine(QueueName = Queue1, /* Name of the Queue - */ /* use the name in push and pop of this Queue */ dataType = n, /* Datatype n - numeric */ /* c - character */ dataLength = 8, /* Length of data iitems */ hashexp = 8, /* Hashexp for the hash - see the documentation for the */ /* hash object. You may want to increase this for */ /* a Queue that will get really large */ rc = Queue1_rc /* return code for Queue operations */ ); /* the macro will create the following data objects and variables */ /* &QueueName._Hash, the hash object used for the Queue */ /* &QueueName._key, the hash object used for the Queue */ /* &QueueName._data, the hash object used for the Queue */ /* &QueueName._old, variable points to the first item put in the queue */ /* &QueueName._new, variable points to the last item put in the queue */ /* &QueueName._len, number of items in the queue */ /* in the Queue */ retain &QueueName._new 0; /* empty Queue has 0 locations in the hash */ retain &QueueName._old 1; /* old will be at location 1 when something is added */ retain &QueueName._len 0; /* empty Queue has 0 items */ length &QueueName._key 8; /* key is numeric count of items in the Queue */ call missing(&QueueName._key); /* explicit assignment so SAS does not complain */ %IF &dataType EQ n %THEN %DO; length &QueueName._data &datalength; retain &QueueName._data 0; %END; %ELSE %DO; length &QueueName._data $ &datalength; retain &QueueName._data ' '; %END; declare hash &QueueName._Hash(hashexp: &hashexp); &rc = &QueueName._Hash.defineKey("&QueueName._key"); &rc = &QueueName._Hash.defineData("&QueueName._data"); &rc = &QueueName._Hash.defineDone(); /* ITEM_SIZE attribute available in SAS 9.2 itemSize = &QueueName._Hash.ITEM_SIZE; */ itemSize = 8 + &datalength; put "Queue &QueueName. Created. Each Item will take " ItemSize " bytes."; %mend QueueDefine; %macro QueueEnqueue(QueueName = Queue1, /* Name of the Queue - */ InputData = Queue1_data, /* Variable containing value to be pushed onto the Queue */ QueueLength = Queue1_length, /* Returns the length of the Queue */ rc = Queue1_rc /* return code for Queue operations */ ); &QueueName._new+1 ; /* item goes at new key in hash */ &QueueName._len+1 ; /* Queue is 1 longer */ &QueueLength = &QueueName._len; &QueueName._key = &QueueName._new; /* new value goes at the end */ &QueueName._data = &InputData; /* value from &InputData */ &rc = &QueueName._Hash.add(); if &rc ne 0 then put "NOTE: Enqueue to Queue &QueueName failed " &InputData= &QueueName._new= ; %mend QueueEnqueue; %macro QueueDequeue(QueueName = Queue1, /* Name of the Queue - */ OutputData = Queue1_data, /* Variable containing value to be removed from the Queue */ QueueLength = Queue1_length, /* Returns the length of the Queue */ rc = Queue1_rc /* return code for Queue operations */ ); if &QueueName._len > 0 then do; &QueueName._key = &QueueName._old; /* return value comes from the oldest location in the hash */ &rc = &QueueName._Hash.find(); if &rc ne 0 then put "NOTE: Dequeue from Queue &QueueName could not find " &QueueName._new= ; &OutputData = &QueueName._data; /* value into &InputData */ /* remove the item from the hash */ &rc = &QueueName._Hash.remove(); if &rc ne 0 then put "NOTE: Dequeue from Queue &QueueName could not remove " &QueueName._new= ; &QueueName._old+1 ; /* item comes from oldest location in the hash */ &QueueName._len+(-1) ; /* Queue is 1 shorter */ &QueueLength = &QueueName._len; end; else do; &rc = 999999; put "NOTE: Cannot Dequeue empty Queue &QueueName into &OutputData "; end; %mend QueueDequeue; %macro QueueLength(QueueName = Queue1, /* Name of the Queue - */ QueueLength = Queue1_length, /* Returns the length of the Queue */ rc = Queue1_rc /* return code for Queue operations */ ); &QueueLength = &QueueName._len; %mend QueueLength; %macro QueueDelete(QueueName = Queue1, /* Name of the Queue - */ rc = Queue1_rc /* return code for Queue operations */ ); &rc = &QueueName._Hash.delete(); if &rc ne 0 then put "NOTE: Cannot delete Queue &QueueName "; %mend QueueDelete; %macro QueueDump(QueueName = Queue1, /* Name of the Queue - */ rc = Queue1_rc /* return code for Queue operations */ ); if &QueueName._len <= 0 then do; put // "Queue &Queuename is empty"; end; /* &QueueName._end <= 0 */ else do; put // " Contents of Queue &Queuename:"; do ixQueue = &QueueName._old to &QueueName._new ; &QueueName._key = ixQueue; &rc = &QueueName._Hash.find(); put "item " ixQueue "value " &QueueName._data; end; /* do ixQueue = QueueName._old to &QueueName._new */ end; /* not &QueueName._end <= 0 */ %mend QueueDump; /* *********************************************************************** */ /* for testing stack */ /* *********************************************************************** */ options nomprint; data test; put /// 'Stack Test' /; if _n_=1 then do; %StackDefine(stackName = testStack, dataType = n, dataLength = 8, hashexp = 8, rc = testStack_rc /* return code for stack operations */ ); put 'define: ' testStack_rc=; end; %StackLength(stackName = testStack, StackLength = testStack_length, rc = testStack_rc ); put / 'length: ' testStack_length = testStack_rc=; %StackDump(stackName = testStack, rc = testStack_rc ); put; do myData = 1 to 5; %StackPush(stackName = testStack, InputData = myData, StackLength = testStack_length, rc = testStack_rc ); put 'push: ' myData= myDataPopped= testStack_length= / ' ' testStack_rc= testStack_end=; end; %StackDump(stackName = testStack, rc = testStack_rc ); put; do ixPop = 1 to 6; %StackPop(stackName = testStack, OutputData = myDataPopped, StackLength = testStack_length, rc = testStack_rc ); put 'pop: ' ixPop= myData= myDataPopped= testStack_length= / ' ' testStack_rc= testStack_end=; end; %StackDump(stackName = testStack, rc = testStack_rc ); put; %StackDelete(stackName = testStack, rc = testStack_rc ); put / 'delete: ' testStack_rc=; run; /* *********************************************************************** */ /* for testing Queue */ /* *********************************************************************** */ options nomprint; data test; put /// 'Queue Test' /; if _n_=1 then do; %QueueDefine(QueueName = testQueue, dataType = n, dataLength = 8, hashexp = 8, rc = testQueue_rc /* return code for Queue operations */ ); put 'define: ' testQueue_rc=; end; %QueueLength(QueueName = testQueue, QueueLength = testQueue_length, rc = testQueue_rc ); put / 'length: ' testQueue_length = testQueue_rc=; %QueueDump(QueueName = testQueue, rc = testQueue_rc ); put; do myData = 1 to 5; %QueueEnqueue(QueueName = testQueue, InputData = myData, QueueLength = testQueue_length, rc = testQueue_rc ); put / 'EnQueue: ' myData= myDataEnQueued= testQueue_length= / ' ' testQueue_rc= testQueue_new= testQueue_old=; end; %QueueDump(QueueName = testQueue, rc = testQueue_rc ); put; do ixQ = 1 to 6; %QueueDequeue(QueueName = testQueue, OutputData = myDataEnQueued, QueueLength = testQueue_length, rc = testQueue_rc ); put / 'deQueue: ' ixQ= myData= myDataEnQueued= testQueue_length= / ' ' testQueue_rc= testQueue_new= testQueue_old=; end; %QueueDump(QueueName = testQueue, rc = testQueue_rc ); put; %QueueDelete(QueueName = testQueue, rc = testQueue_rc ); put / 'delete: ' testQueue_rc=; run;