Files
SAS_PACKAGES/packages/dfa.md
yabwon 222d88511a DFA, version 0.3
DFA, version 0.3
-documentation modified, dfa.md file added
2020-11-30 14:30:12 +01:00

44 KiB


The DFA package [ver. 0.3]

The DFA (a.k.a. Dynamic Function Array) package implements:

  • dynamic numeric and character arrays,
  • dynamic stacks (filo),
  • dynamic queues (fifo),
  • dynamic ordered stacks,
  • priority queues.

The package provides a set of macros, which allows to generate call routines simulating data structures mentioned above.

Few exemplary functions are also generated. See particular macro help for further details.


Package contains:

  1. macro createdfarray
  2. macro createdharray
  3. macro createdhfifo
  4. macro createdhordstack
  5. macro createdhprtqueue
  6. macro createdhstack
  7. exec generatearrays
  8. clean generatearrays

SAS package generated by generatePackage, version 20201115

The SHA256 hash digest for package BasePlus: 1FC8D030D576C33F1B5DEB27E17534946209BC148D57A1357CA025ED1E69AEB8


Content description

>>> %createDFArray() macro: <<<

The %createDFArray() macro allows to generate a dynamic function array which is a FCMP based approach to create dynamically allocated numeric array with possible values searching and WHICHN() function emulation.

Note: Arrays provided by the macro are one dimensional arrays.

SYNTAX:

The basic syntax is the following, the <...> means optional parameters:

%createDFArray( 
  arrayName           
 <,debug=0>
 <,simple=0>
 <,resizeFactor=0>
 <,outlib=work.DFAfcmp.package>
 <,hashexp=13>
)

Arguments description:

  1. arrayName - Required, creates a FCMP call subroutine which is also an array name. In the data step it is used in form of a call subroutine, e.g. call arrayName("Allocate", -3, 3). Has to satisfy FCMP function naming requirements, but with maximum of 24 characters.
  • debug= - Optional, the default value is 0. If set to 1 then it turns on a debugging mode.

  • simple= - Optional, the default value is 0. A simple dynamic function array is one which is not searchable and does not allows to use which() functionality. If set to 1 then it disables SEARCH and WHICH functionality. See examples below for details.

  • resizeFactor= - Optional, the default value is 0. If set to 0 then the dynamic array size is not changeable(mutable) after initial size allocation. If set not to 0 then arrays dimensions are mutable after allocation, i.e. even if an array is allocated for ten elements (like A[1:10]) you can do A[17] = 42 and it will resize itself dynamically. Hint! Set to, e.g. 4999, for faster allocation process. See examples below for details.

  • outlib= - Optional, the default value is work.DFAfcmp.package. It points the default location for new generated dynamic function arrays compiled by FCMP. Hint! Keep it as it is.

  • hashexp= - Optional, the default value is 13. It is the default hashexp= value for internal hash table used by the function.

Created function arguments description:

A function generated by the macro is:

call &arrayName.(IO, position, value)

and accepts the following list of arguments and values:

  1. IO - is a character steering argument, possible values and behaviour they call are the following:
  • O, Output, R, Return - to get the data from an array,
  • I, Input - to insert the data into an array,
  • +, Add - to increment given position by a value,
  • C, Clear - to reduce an array to a single empty cell,
  • A, Allocate - to reserve space for array width and set starting values,
  • D, Dimension - to return minimal and maximal index of an array,
  • F, Find, Exist - to find out if a given value exist in an array,
  • W, Which - to search the first position of data in array, WHICHN() function emulator,
  • Sum - to return the sum of non-missing elements of an array,
  • Nonmiss - to return the number of non-missing elements of an array,
  • Avg, Mean, Average - to return the average of non-missing elements of an array,
  • Min, Minimum - to return the minimum of non-missing elements of an array,
  • Max, Maximum - to return the maximum of non-missing elements of an array.
  1. position - is a numeric argument and depends on the IO value. Behaves in the following way:
  • for O, Output, R, Return/ I, Input/ +, Add it takes an arrays index from (into) which data is get (put),
  • for C, Clear is ignored,
  • for A, Allocate sets the value of the minposition, i.e. the minimal position of the array index,
  • for D, Dimension it returns value of the minposition,
  • for Sum, Nonmiss, Avg, Mean, Average, Min, Minimum, Max, and Maximum is ignored,
  • for F, Find, Exist it returns number of occurrences of a given value in an array,
  • for W, Which it returns position the first occurrence of a given value in an array.

.3 value - is a numeric argument and depends on the IO value. Behaves in the following way:

  • for O, Output, R, Return it holds value retrieved from an array on a given position,
  • for I, Input it holds the value inserted into an array on a given position,
  • for +, Add it holds the value that is used to increment an array value at a given position,
  • for C, Clear is ignored,
  • for A, Allocate it sets the value of the maxposition, i.e. maximal position of the array index,
  • for D, Dimension it returns value of the maxposition
  • for Sum, Nonmiss, Avg, Mean, Average, Min, Minimum, Max, and Maximum it returns the calculated summary value,
  • for F, Find, Exist, W, Which is the value to be searched in an array.

The position and the value arguments are outargs, i.e. can be changed by the function.

EXAMPLES AND USECASES:

EXAMPLE 1. Dynamic, Searchable, and Immutable array:

  %createDFArray(ArrDSI);
  options APPEND=(cmplib = WORK.DFAfcmp) ;

  data Example1;
    call ArrDSI("Allocate", 1, 10);
    L = 0; H = 0;
    call ArrDSI("Dim", L, H);
    put L= H=;

 * populate array with data ;
    do i = L to H;
      call ArrDSI("Input", i, i**2); 
    end;

 * searchability allows to find number of occurrences of value in the array ;
    F = .;
    call ArrDSI("Find", F, 16);
    put "Value 16 occurs " F "times";
    call ArrDSI("Find", F, 17);
    put "Value 17 occurs " F "times";

 * increase value of cell 4 by 1, and verify at WHICH position is 17 (by searchability);
    call ArrDSI("+", 4, 1);
    call ArrDSI("Which", F, 17);
    put "Value 17 occurred for the first time at position " F;
  
 * get values from the array ;
    Value = .;
    do i = L to H;
      call ArrDSI("Output", i, Value); 
      put i= Value=;
    end;

 * some basic statistics ;
    call ArrDSI("Sum", ., STAT); put "sum = " STAT;
    call ArrDSI("Avg", ., STAT); put "avg = " STAT;
    call ArrDSI("Min", ., STAT); put "min = " STAT;
    call ArrDSI("Max", ., STAT); put "max = " STAT;
    call ArrDSI("Cnt", ., STAT); put "cnt = " STAT;

 * immutability does _not_ allow to increase dimensions automatically;
 * this line returns an error ;
    call ArrDSI("Input", 42, -1); 
  run;

EXAMPLE 2. Dynamic, Searchable, and Mutable array:

  %createDFArray(ArrDSM, resizefactor=17);
  options APPEND=(cmplib = WORK.DFAfcmp) ;

  data Example2;
    call ArrDSM("Allocate", -2, 2);
  
    do i = -2 to 2;
      call ArrDSM("Input", i, 2**i);
    end;
    
    L = .; H = .;
    call ArrDSM("Dim", L, H);
    put L= H=;

 * mutability allows to increase dimensions automatically
 * create index 3 and -3;
    call ArrDSM("+", 3, 8);
    call ArrDSM("+",-3, 0.125);
    call ArrDSM("Dim", L, H);
    put L= H=;
    
    Value = .;
    do i = L to H;
      call ArrDSM("O", i, Value);
      put i= Value=;
    end;

  run;

EXAMPLE 3. Dynamic, non-searchable (a.k.a. SiMPle), and Immutable array:

  %createDFArray(ArrDSMPLI, simple=1);
  options APPEND=(cmplib = WORK.DFAfcmp) ;

  data Example3;
    call ArrDSMPLI("Allocate", -2, 2);
  
    do i = -2 to 2;
      call ArrDSMPLI("Input", i, 2**i);
    end;

 * non-searchable array (a.k.a. simple) does not allow ;
 * to find number of occurrences of value in the array ;
 * and verify what is the first position of a value ;
 * this lines return a warning ;
    call ArrDSMPLI("Exist", i, 1);
    call ArrDSMPLI("Which", i, 1);
  run;

EXAMPLE 4. Dynamic, non-searchable (a.k.a. SiMPle), and Mutable array:

  %createDFArray(ArrDSMPLM, simple=1, resizefactor=42);
  options APPEND=(cmplib = WORK.DFAfcmp) ;

  data Example4;
    call ArrDSMPLM("Allocate", 1, 1);
  
 * mutability allows to increase dimensions automatically ;
    do i = -12 to 12;
      call ArrDSMPLM("Input", i, i*2);
    end;

 * non-searchable array (a.k.a. simple) does not allow ;
 * to find number of occurrences of value in the array ;
 * and verify what is the first position of a value ;
 * this lines return a warning ;
    i = .;
    call ArrDSMPLM("Exist", i, -24);
    put "Exist " i=;
    call ArrDSMPLM("Which", i,  24);
    put "Which " i=;
  run;

>>> %createDHArray() macro: <<<

The %createDHArray() macro allows to generate a dynamic hash array which is a FCMP based approach to create dynamically allocated numeric or character array

Note: Arrays provided by the macro are one dimensional arrays.

SYNTAX:

The basic syntax is the following, the <...> means optional parameters:

%createDHArray( 
  arrayName           
 <,debug=0>
 <,type=8>
 <,outlib=work.DFAfcmp.package>
 <,hashexp=13>
)

Arguments description:

  1. arrayName - Required, creates a FCMP call subroutine which is also an array name. In the data step it is used in form of a call subroutine, e.g. call arrayName("Allocate", -3, 3). Has to satisfy FCMP function naming requirements, but with maximum of 24 characters.
  • debug= - Optional, the default value is 0. If set to 1 then it turns on a debugging mode.

  • type= - Optional, the default value is 8. Indicates what type (numeric/character) and length are data portion of generated array. Should be in line with the LENGTH statement, e.g. 8, $ 30, etc. Determines if the value argument is numeric or character.

  • outlib= - Optional, the default value is work.DFAfcmp.package. It points the default location for new generated dynamic function arrays compiled by FCMP. Hint! Keep it as it is.

  • hashexp= - Optional, the default value is 13. It is the default hashexp= value for internal hash table used by the function.

Created function arguments description:

A function generated by the macro is:

call &arrayName.(IO, position, value)

and accepts the following list of arguments and values:

  1. IO - is a character steering argument, possible values and behaviour they call are the following:
  • O, Output, R, Return - to get the data from an array,
  • I, Input - to insert the data into an array,
  • C, Clear - to reduce an array to a single empty cell,
  • L, Low, Lower, Lbound - to return minimal position of index,
  • H, High, Higher, Hbound - to return maximal position of index.
  1. position - is a numeric argument and depends on the IO value. Behaves in the following way:
  • for O, Output, R, Return/ I, and Input it is an array index from (into) which data is get (put),
  • for C it is ignored,
  • for L, Low, Lower, and Lbound it returns the first position of an index,
  • for H, High, Higher, and Hbound it returns the last position of an index,
  • otherwise is not modified.
  1. value - is a numeric or character argument (determined by the type=) and depends on the IO value. Behaves in the following way:
  • for O, Output, R, and Return it holds value retrieved from an array from a given position,
  • for I, Input it holds the value inserted into an array into a given position,
  • for C is ignored,
  • for L, Low, Lower, and Lbound returns first value of index,
  • for H, High, Higher, and Hbound returns last value of index,
  • otherwise is not modified.

The position and the value arguments are outargs, i.e. can be changed by the function.

EXAMPLES AND USECASES:

EXAMPLE 1. Dynamic, Hash-based, and Character array:

  %createDHArray(ArrDHC, type = $ 12); 
  options APPEND=(cmplib = WORK.DFAfcmp) ; 
 
  %let zeros = 6; *[to test bigger sizes];
  data Example1; 
   
    t = time(); 
    do _I_ = -1e&zeros. to 1e&zeros.; 
      _X_ = put(_I_*10, z12.); 
      call ArrDHC("Input", _I_, _X_); 
    end; 
    t = time() - t; 
    put t= / _X_= /; 
   
 * get the size info ; 
    LB = 0; HB = 0; 
    drop LB HB; 
      call ArrDHC('Lbound', LB, _X_); 
      call ArrDHC('Hbound', HB, _X_); 
    put LB= HB= /; 
   
    t = time(); 
    do _I_ = HB + 1 to LB - 1 by -1; 
      call ArrDHC('Output', _I_, _X_); 
      output;  
    end; 
    t = time() - t; 
    put t= / _X_= /; 
    
 * clear for further reuse ; 
    call ArrDHC('C', ., ''); 
    
  run;

EXAMPLE 2. Dynamic, Hash-based, and Numeric array:

  %createDHArray(ArrDHN);
  options APPEND=(cmplib = WORK.DFAfcmp) ;

  data Example2;
  
    do i = -2 to 2;
      call ArrDHN("Input", i, 2**i);
    end;

    do i = -2 to 2;
      call ArrDHN("+", i, -10);
    end;
    
    v = .;
    do i = -2 to 2;
      call ArrDHN("Output", i, v);
      put i= v=;
    end;

  run;

>>> %createDHFifo() macro: <<<

The %createDHFifo() macro allows to generate a dynamic hash fifo which is a FCMP based approach to create dynamically allocated numeric or character "first in first out" queue

Interesting reading about implementing a fifo via hash table can be found in chapter 10.4 of the: "Data Management Solutions Using SAS Hash Table Operations: A Business Intelligence Case Study" book by Paul Dorfman and Don Henderson.

SYNTAX:

The basic syntax is the following, the <...> means optional parameters:

%createDHFifo( 
  fifoName           
 <,debug=0>
 <,type=8>
 <,outlib=work.DFAfcmp.package>
 <,hashexp=13>
)

Arguments description:

  1. fifoName - Required, creates a FCMP call subroutine which is also a fifo name. In the data step it is used in form of a call subroutine, e.g. call fifoName("Enqueue", 3). Has to satisfy FCMP function naming requirements, but with maximum of 24 characters.
  • debug= - Optional, the default value is 0. If set to 1 then it turns on a debugging mode.

  • type= - Optional, the default value is 8. Indicates what type (numeric/character) and length are data portion of generated array. Should be in line with the LENGTH statement, e.g. 8, $ 30, etc. Determines if the value argument is numeric or character.

  • outlib= - Optional, the default value is work.DFAfcmp.package. It points the default location for new generated dynamic function arrays compiled by FCMP. Hint! Keep it as it is.

  • hashexp= - Optional, the default value is 13. It is the default hashexp= value for internal hash table used by the function.

Created function arguments description:

A function generated by the macro is:

call &fifoName.(IO, value)

and accepts the following list of arguments and values:

  1. IO - is a character steering argument, possible values and behaviour they call are the following:
  • O, Output, D, Dequeue, R, Return - to get the data from a fifo (and remove it from the fifo)
  • I, Input, E, Enqueue, and Insert - to insert the data into a fifo
  • C, Clear - to reduce a fifo to an empty one
  • P, Peek, T, and Tail - to peek the data from a fifo (and NOT remove it from the fifo)
  • H, Head - to peek the data from a fifo head (and NOT remove it from the fifo)
  • Sum - returns sum of nonmissing numeric elements of a stack
  • Avg, Mean, Average - returns average of nonmissing numeric elements of a stack
  • Nonmiss, Cnt - returns number of nonmissing elements of a stack
  • Height - returns height a stack
  1. value - is a numeric or character argument (determined by the type=) and depends on the IO value. Behaves in the following way:
  • for O, Output, D, Dequeue, R, Return it holds the value popped from the fifo,
  • for I, Input, E, Enqueue, Insert it holds the value to be pushed into the fifo,
  • for C, Clear it is ignored,
  • for P, Peek holds the value peeked from the fifo,
  • for Sum, Nonmiss, Cnt, Avg, Mean, Average, and Height it returns calculated summary value.

The value argument is outarg, i.e. can be changed by the function.

EXAMPLES AND USECASES:

EXAMPLE 1. Dynamic, Hash-based, and Character fifo:

  %createDHFifo(FifoDHC, type = $ 12); 
  options APPEND=(cmplib = WORK.DFAfcmp) ; 

 
  %let zeros = 6; *[to test bigger sizes];
  data Example1;
 
    t = time(); drop t; 
    do _I_ = 1 to 1e&zeros.; 
      _X_ = put(_I_*10, z12.); 
      call FifoDHC("Enqueue", _X_); 
    end; 
    t = time() - t;
 
    call FifoDHC("Height", _X_); 
    put t= / _X_=; 
   
    t = time(); 
    do _I_ = 1 to 1e&zeros. + 3; 
      call FifoDHC('Dequeue', _X_); 
      output;  
    end; 
    t = time() - t; 

    call FifoDHC("Height", _X_); 
    put t= / _X_=; 
 
    %* clear for further reuse *; 
    call FifoDHC('Clear', '');  

  run;

EXAMPLE 2. Dynamic, Hash-based, and Numeric fifo:

  %createDHFifo(FifoDHN); 
  options APPEND=(cmplib = WORK.DFAfcmp) ; 

  data Example2;
 
    do _I_ = 1,.,2,.,3,.,4,.,5,.,6;  
      call FifoDHN("E", _I_); 
    end; 

    call FifoDHN("Sum", _I_); 
    put "Sum    " _I_=;

    call FifoDHN("Avg", _I_); 
    put "Avg    " _I_=;

    call FifoDHN("Cnt", _I_); 
    put "Cnt    " _I_=;
   
    call FifoDHN("Height", _I_); 
    put "Height " _I_=; 

    call FifoDHN("Tail", _I_); 
    put "Tail of fifo is " _I_=; 

    call FifoDHN("Height", _I_); 
    put "Height after Tail " _I_=; 

    call FifoDHN("Head", _I_); 
    put "Head of fifo is " _I_=; 

    call FifoDHN("Height", _I_); 
    put "Height after Head" _I_=; 

    _X_ = 0;
    do _I_ = 1 to _I_; 
      call FifoDHN('D', _X_); 
      output;  
    end; 

    call FifoDHN("Height", _I_); 
    put "Height " _I_=;

  run;

>>> %createDHOrdStack() macro: <<<

The %createDHOrdStack() macro allows to generate a dynamic ORDERED hash stack which is a FCMP based approach to create dynamically allocated numeric or character ordered stack

Interesting reading about implementing a stack via hash table can be found in chapter 10.4 of the: "Data Management Solutions Using SAS Hash Table Operations: A Business Intelligence Case Study" book by Paul Dorfman and Don Henderson.

SYNTAX:

The basic syntax is the following, the <...> means optional parameters:

%createDHOrdStack( 
  fifoName           
 <,debug=0>
 <,type=8>
 <,order=A>
 <,outlib=work.DFAfcmp.package>
 <,hashexp=13>
)

Arguments description:

  1. stackName - Required, creates a FCMP call subroutine which is also a stack name. In the data step it is used in form of a call subroutine, e.g. call stackName("Push", 3). Has to satisfy FCMP function naming requirements, but with maximum of 24 characters.
  • debug= - Optional, the default value is 0. If set to 1 then it turns on a debugging mode.

  • type= - Optional, the default value is 8. Indicates what type (numeric/character) and length are data portion of generated array. Should be in line with the LENGTH statement, e.g. 8, $ 30, etc. Determines if the value argument is numeric or character.

  • order= - Optional, the default value is A. Indicates a method of ordering of the stack, allowed values are: A for ascending and D for descending.

  • outlib= - Optional, the default value is work.DFAfcmp.package. It points the default location for new generated dynamic function arrays compiled by FCMP. Hint! Keep it as it is.

  • hashexp= - Optional, the default value is 13. It is the default hashexp= value for internal hash table used by the function.

Created function arguments description:

A function generated by the macro is:

call &stackName.(IO, value)

and accepts the following list of arguments and values:

  1. IO - is a character steering argument, possible values and behaviour they call are the following:
  • O, Output, Pop, G, Get, R, Return - to get the data from a stack (and remove it from the top),
  • I, Input, Push, Put, Insert - to insert the data into a stack,
  • C, Clear - to reduce a stack to an empty one,
  • P, Peek - to peek the data from a stack (and NOT remove it from the top),
  • Sum - returns sum of non-missing numeric elements of a stack,
  • Avg, Mean, Average - returns average of non-missing numeric elements of a stack,
  • Nonmiss, Cnt, Nnm - returns number of non-missing elements of a stack,
  • Height - returns height a stack,
  • Min, Minimum - returns minimum of non-missing elements of a stack,
  • Max, Maximum - returns maximum of non-missing elements of a stack.
  1. value - is a numeric or character argument (determined by the type=) and depends on the IO value. Behaves in the following way:
  • for O, Output, Pop, G, Get, R, Return it holds a value popped from a stack,
  • for I, Input, Push, Put, Insert it holds a value to be pushed into a stack,
  • for C, Clear it is ignored,
  • for P, Peek it holds a value peeked from a stack,
  • for Sum, Nonmiss, Cnt, Avg, Mean, Average, Height, Min, Minimum, Max, and Maximum it returns calculated summary value,

The value argument is outarg, i.e. can be changed by the function.

EXAMPLES AND USECASES:

EXAMPLE 1. Dynamic, Hash-based, and Character Descending Ordered stack:

  %createDHOrdStack(DescStackC, type = $ 12, order=D); 
  options APPEND=(cmplib = WORK.DFAfcmp) ;
 
  data Example1; 
    
    do _X_ = "A","B"," ","C","A"," ","B","C"; 
      call DescStackC("Push", _X_); 
    end; 
  
    length S $ 12;
    call DescStackC('Height', S); 
    put 'Height ' S;

    do until(strip(S) = "0"); 
      call DescStackC('Get', _X_); 
      call DescStackC('Height', S);
      put S= _X_=;
      output;
    end; 
    
    %* clear for further reuse *; 
    call DescStackC('Clear',''); 

  run; 

EXAMPLE 2. Dynamic, Hash-based, and Numeric Ascending Ordered stack:

  %createDHOrdStack(DescStackN, order=A); 
  options APPEND=(cmplib = WORK.DFAfcmp) ;
 
  data Example2; 
   
    call missing(Sum, Avg, Min, Max, Cnt, Hgt, Peek);
    do _X_ = 1,6,2,.,5,3,4; 
      call DescStackN("Put", _X_); 
      call DescStackN('Sum', Sum);
      call DescStackN('Avg', Avg);
      call DescStackN('Min', Min);
      call DescStackN('Max', Max);
      call DescStackN('Cnt', Cnt);
      call DescStackN('Height', Hgt);
      put (_ALL_) (=);  
    end; 
   
    call DescStackN('Peek', Peek); 
    put Peek=;  

    do _I_ = 1 to Hgt; 
      call DescStackN('Output', _X_);
      keep _X_; 
      if _X_ > .z then output; 
    end; 
   
    call DescStackN('Peek', Peek); 
    put Peek=;
   
    %* clear for further reuse *; 
    call DescStackN('Clear',.);  

  run; 

>>> %createDHPrtQueue() macro: <<<

The %createDHPrtQueue() macro allows to generate a dynamic PRIORITY hash queue which is a FCMP based approach to create dynamically allocated numeric or character priority queue

SYNTAX:

The basic syntax is the following, the <...> means optional parameters:

%createDHPrtQueue( 
  fifoName           
 <,debug=0>
 <,type=8>
 <,newOnTop=+>
 <,outlib=work.DFAfcmp.package>
 <,hashexp=13>
)

Arguments description:

  1. queueName - Required, creates a FCMP call subroutine which is also a queue name. In the data step it is used in form of a call subroutine, e.g. call queueName("Bottom", 1, 3). Has to satisfy FCMP function naming requirements, but with maximum of 24 characters.
  • debug= - Optional, the default value is 0. If set to 1 then it turns on a debugging mode.

  • type= - Optional, the default value is 8. Indicates what type (numeric/character) and length are data portion of generated array. Should be in line with the LENGTH statement, e.g. 8, $ 30, etc. Determines if the value argument is numeric or character.

  • newOnTop= - Optional, the default value is +. Indicates how to keep order in the same priority group, allowed values are + or -. Plus(+) sets new elements at the top of the group, minus(-) at the bottom.

  • outlib= - Optional, the default value is work.DFAfcmp.package. It points the default location for new generated dynamic function arrays compiled by FCMP. Hint! Keep it as it is.

  • hashexp= - Optional, the default value is 13. It is the default hashexp= value for internal hash table used by the function.

Created function arguments description:

A function generated by the macro is:

call &queueName.(IO, position, value)

and accepts the following list of arguments and values:

  1. IO - is a character steering argument, possible values and behaviour they call are the following:
  • O, Output, D, Dequeue, R, Return - it pops/gets/outputs the data from the queue head (high priority),
  • B, Bottom - it pops/gets/outputs the data from the queue tail (low priority),
  • I, Input, E, Enqueue, Insert - it push/puts/inserts the data into the queue,
  • C, Clear - it reduces a queue to an empty one,
  • H, Head - it peeks the data from the queue head and NOT removes it,
  • T, Tail - it peeks the data from the queue tail and NOT removes it,
  • Sum - it returns sum of non-missing numeric elements of the queue,
  • Avg, Mean, Average - it returns average of non-missing numeric elements of the queue,
  • Nonmiss, Cnt - it returns number of non-missing elements of the queue,
  • Height - it returns height of the queue.
  1. position - is a numeric argument and depends on the IO value. Behaves in the following way:
  • for O, Output, D, Dequeue, R, Return and B, Bottom, or H, Head, T, Tail it holds a priority level of value popped from the queue,
  • for I, Input, E, Enqueue, Insert it holds a priority level of value to be pushed into the queue,
  • for C ignored,
  • for numeric queue and Sum, Nonmiss, Cnt, Avg, Mean, Average, Height returns calculated summary value.
  1. value - is a numeric or character argument (determined by the type=) and depends on the IO value. Behaves in the following way:
  • for O, Output, D, Dequeue, R, Return and B, Bottom or H, Head, T, Tail it holds a value popped from the queue,
  • for I, Input, E, Enqueue, Insert it holds a value to be pushed into the queue,
  • for C ignored,
  • for numeric queue and Sum, Nonmiss, Cnt, Avg, Mean, Average, Height returns calculated summary value,
  • otherwise does not modify value.

The position and the value arguments are outargs, i.e. can be changed by the function.

EXAMPLES AND USECASES:

EXAMPLE 1. Dynamic, Hash-based, and Character Priority queue:

  %createDHPrtQueue(PriorityQueuePC, type = $ 12, newOnTop=+);
  %createDHPrtQueue(PriorityQueueNC, type = $ 12, newOnTop=-); 
  options APPEND=(cmplib = WORK.DFAfcmp) ; 

  data Example1; 
   
    _I_ = .; 
    length _X_ _Y_ $ 3;
    do _X_ = "AAA","BBB","CCC","AA","BB","CC","A","B","C";
      _I_ + 1; 
      call PriorityQueuePC("I", mod(_I_, 3), _X_);
      call PriorityQueueNC("I", mod(_I_, 3), _X_); 
    end; 
   
    Height = .;
    call PriorityQueuePC('Height', Height, ''); 
    put Height=;

    do until(Height = 0); 
      call PriorityQueuePC('Dequeue', _I_, _X_);
      call PriorityQueueNC("Dequeue", _I_, _Y_);
      call PriorityQueueNC('Height', Height, '');
      put (_ALL_) (=); 
      output;  
    end; 
 
  run;

EXAMPLE 2. Dynamic, Hash-based, and Numeric Priority queue:

  %createDHPrtQueue(PriorityQueueN);
  options APPEND=(cmplib = WORK.DFAfcmp) ; 

  data Example2; 
   
    do _X_ = -5 to 5; 
      call PriorityQueueN("Enqueue", abs(_X_), _X_);
    end; 
   
    call missing(Sum, Avg, Cnt, Hgt); 
    call PriorityQueueN('Sum',    ., Sum);
    call PriorityQueueN('Avg',    ., Avg);
    call PriorityQueueN('Cnt',    ., Cnt);
    call PriorityQueueN('Height', ., Hgt);
    put (_ALL_) (=);

    do _N_ = 1 to Hgt; 
      call PriorityQueueN("Dequeue", _X_, _Y_);
      put _X_= _Y_=;
    end;  
  
  run;

>>> %createDHStack() macro: <<<

The %createDHStack() macro allows to generate a dynamic hash stack which is a FCMP based approach to create dynamically allocated numeric or character stack

Interesting reading about implementing a stack via hash table can be found in chapter 10.4 of the: "Data Management Solutions Using SAS Hash Table Operations: A Business Intelligence Case Study" book by Paul Dorfman and Don Henderson.

SYNTAX:

The basic syntax is the following, the <...> means optional parameters:

%createDHStack( 
  fifoName           
 <,debug=0>
 <,type=8>
 <,outlib=work.DFAfcmp.package>
 <,hashexp=13>
)

Arguments description:

  1. stackName - Required, creates a FCMP call subroutine which is also a stack name. In the data step it is used in form of a call subroutine, e.g. call stackName("Push", 3). Has to satisfy FCMP function naming requirements, but with maximum of 24 characters.
  • debug= - Optional, the default value is 0. If set to 1 then it turns on a debugging mode.

  • type= - Optional, the default value is 8. Indicates what type (numeric/character) and length are data portion of generated array. Should be in line with the LENGTH statement, e.g. 8, $ 30, etc. Determines if the value argument is numeric or character.

  • outlib= - Optional, the default value is work.DFAfcmp.package. It points the default location for new generated dynamic function arrays compiled by FCMP. Hint! Keep it as it is.

  • hashexp= - Optional, the default value is 13. It is the default hashexp= value for internal hash table used by the function.

Created function arguments description:

A function generated by the macro is:

call &stackName.(IO, value)

and accepts the following list of arguments and values:

  1. IO - is a character steering argument, possible values and behaviour they call are the following:
  • O, Output, Pop, G, Get, R, Return - to get the data from a stack (and remove it from the top),
  • I, Input, Push, Put, Insert - to insert the data into a stack,
  • C, Clear - to reduce a stack to an empty one,
  • P, Peek - to peek the data from a stack (and NOT remove it from the top),
  • Sum - returns sum of non-missing numeric elements of a stack,
  • Avg, Mean, Average - returns average of non-missing numeric elements of a stack,
  • Nonmiss, Cnt, Nnm - returns number of non-missing elements of a stack,
  • Height - returns height a stack,
  1. value - is a numeric or character argument (determined by the type=) and depends on the IO value. Behaves in the following way:
  • for O, Output, Pop, G, Get, R, Return it holds a value popped from a stack,
  • for I, Input, Push, Put, Insert it holds a value to be pushed into a stack,
  • for C, Clear it is ignored,
  • for P, Peek it holds a value peeked from a stack,
  • for Sum, Nonmiss, Cnt, Avg, Mean, Average, Height it returns calculated summary value,

The value argument is outarg, i.e. can be changed by the function.

EXAMPLES AND USECASES:

EXAMPLE 1. Dynamic, Hash-based, and Character stack:

  %createDHStack(StackDHC, type = $ 12); 
  options APPEND=(cmplib = WORK.DFAfcmp) ; 

 
  %let zeros = 6; *[to test bigger sizes];
  data Example1;
 
    t = time(); drop t; 
    do _I_ = 1 to 1e&zeros.; 
      _X_ = put(_I_*10, z12.); 
      call StackDHC("Put", _X_); 
    end; 
    t = time() - t;
 
    call StackDHC("Height", _X_); 
    put t= / _X_=; 
   
    t = time(); 
    do _I_ = 1 to 1e&zeros. + 3; 
      call StackDHC('Pop', _X_); 
      output;  
    end; 
    t = time() - t; 

    call StackDHC("Height", _X_); 
    put t= / _X_=; 
 
    %* clear for further reuse *; 
    call StackDHC('Clear', '');  

  run;

EXAMPLE 2. Dynamic, Hash-based, and Numeric stack:

  %createDHStack(StackDHN); 
  options APPEND=(cmplib = WORK.DFAfcmp) ; 

  data Example2;
 
    do _I_ = 1,.,2,.,3,.,4,.,5,.,6;  
      call StackDHN("Put", _I_); 
    end; 

    call StackDHN("Sum", _I_); 
    put "Sum    " _I_=;

    call StackDHN("Avg", _I_); 
    put "Avg    " _I_=;

    call StackDHN("Cnt", _I_); 
    put "Cnt    " _I_=;
   
    call StackDHN("Height", _I_); 
    put "Height " _I_=; 

    _X_ = 0;
    do _I_ = 1 to _I_; 
      call StackDHN('Pop', _X_); 
      output;  
    end; 

    call StackDHN("Height", _I_); 
    put "Height " _I_=;

  run;

>>> generateArrays exec: <<<

The generateArrays exec file provides a list of automatically generated examples of functions emulating data structures.

The list of provided examples is the following:

  • SmpArray - Simple Immutable Dynamic Function Array
  • SmpMtbArray - Simple Mutable Dynamic Function Array
  • SrchArray - Searchable Immutable Dynamic Function Array
  • SrchMtbArray - Searchable Mutable Dynamic Function Array
  • DynArrayC - Dynamic Hash-based Character Function Array (length 256 bytes)
  • StackC - Dynamic Hash-based Character Stack (length 256 bytes)
  • StackN - Dynamic Hash-based Numeric Stack
  • FifoC - Dynamic Hash-based Character Fifo (length 256 bytes)
  • FifoN - Dynamic Hash-based Character Fifo
  • DescStackC - Dynamic Hash-based Character Descending Ordered Stack (length 256 bytes)
  • AscStackC - Dynamic Hash-based Character Ascending Ordered Stack (length 256 bytes)
  • DescStackN - Dynamic Hash-based Numeric Descending Ordered Stack
  • AscStackN - Dynamic Hash-based Numeric Ascending Ordered Stack
  • PrtQueueNTC - Dynamic Hash-based Character Priority Queue with New on Top (length 256 bytes)
  • PrtQueueNBC - Dynamic Hash-based Character Priority Queue with New on Bottom (length 256 bytes)
  • PrtQueueNTN - Dynamic Hash-based Numeric Priority Queue with New on Top
  • PrtQueueNBN - Dynamic Hash-based Numeric Priority Queue with New on Bottom

The outlib= option is set to work.DFAfcmp.package. The cmplib= option is updated automatically.


>>> generateArrays clean: <<<

The generateArrays clean file clears the list of automatically generated examples of functions emulating data structures provided in the generatearrays.sas exec file.

The cmplib= option is updated automatically.


License

Copyright (c) 2019 Bartosz Jablonski

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.