- [The DFA package](#dfa-package)
- [Content description](#content-description)
* [`%createDFArray()` macro](#createdfarray-macro)
* [`%createDHArray()` macro](#createdharray-macro)
* [`%createDHFifo()` macro](#createdhfifo-macro)
* [`%createDHOrdStack()` macro](#createdhordstack-macro)
* [`%createDHPrtQueue()` macro](#createdhprtqueue-macro)
* [`%createDHStack()` macro](#createdhstack-macro)
* [`generateArrays` exec](#createdhprtqueue-exec)
* [`generateArrays` clean](#createdhprtqueue-clean)
* [License](#license)
---
# 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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
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.
2. `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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
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.
2. `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.
3. `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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%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](https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics))
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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
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
2. `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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%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](https://en.wikipedia.org/wiki/Stack_(abstract_data_type))
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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
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.
2. `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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%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](https://en.wikipedia.org/wiki/Priority_queue)
### SYNTAX: ###################################################################
The basic syntax is the following, the `<...>` means optional parameters:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
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.
2. `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.
3. `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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%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](https://en.wikipedia.org/wiki/Stack_(abstract_data_type))
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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
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,
2. `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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%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:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%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.
---