Dynamic Programming in SAS with CALL EXECUTE
When you say dynamic programming in SAS, most programmers think of the macro language. However, sometimes it is not the appropriate tool. If you want to write data driven code, you should familiarize yourself with the CALL Execute Routine. This post serves as an introduction to Call Execute logic and how to use it to your advantage. I will demonstrate a few examples of how to use it as well.
The Call Execute Routine
The Call Execute Routine accepts a single argument. The argument has to be a character expression. Either as a hard coded string, a data step character variable or a macro variables, that resolves to a character expression. Furthermore, the argument can be any concatenation of the mentioned character expressions. The expressions are evaluated in the data step and the resulting code is pushed into an input stack. Consequently, Call Execute builds the code as the data step iterates, and pushes the code into an input stack, that executes after the data step terminates. This is very important to understand, so I will repeat it: The resulting code is executed after the data step that builds the code terminates. Consequently, the data step serves merely as a code generator. Not too different from the macro facility.
A Simple Example
Let us look at a simple example of how to use the Call Execute Routine. In the code below, I have three statements. Each of then contains a character string with a simple SAS exepression. When each expression is processed, the code is pushed into a queue, that executes after the data step has terminated. First in first out (FIFO). SAS issues a note in the log and shows the generated code.
After the data step terminates, the generated code executes. In this case, it simply executes a data step to create a copy of the SASHELP.CLASS data step.
data callstack; call execute("data class;"); call execute("set sashelp.class;"); call execute("run;"); run;
Now let us take this a bit further. In the code above, I simply hard code SAS code into the Routine. Next, let us utilize the dynamic nature of the Routine. First, I create the data set ds with three observations. Next, I use the data step and set the ds data set. Then I use the Call Execute Routine to create a data set for each observation in ds. I use the Cat Function to concatenate the character expressions, which now consist of both hard coded text and the name variable from ds. Furthermore, I use the Compbl Function to ensure that the code looks nice when it is presented in the log. In the first iteration, the name variable resolves to “one”. In the second iteration, it resolves to “Two” and so on.
data ds; input name $; datalines; one two three ; data _null_; set ds; call execute (compbl(cat( "data ", name, "; set sashelp.class; run;" ))); run;
As you can see in the log, the code that is pushed to the queue is equivalent to the code below.
data one; set sashelp.class; run; data two; set sashelp.class; run; data three; set sashelp.class; run;
Lastly, let me mention that in the above explanations, I do simplify things a little. Because, I assume that the argument passed to the Call Execute Routine does not contain macro code. If it does, then things get a little more complicated. And suddenly, it even matters whether the macro variables are enclosed in single or double quotation marks. You can read more about those situations in the blog post CALL EXECUTE made easy for SAS data-driven programming.
In this post, I introduce the Call Execute Routine and present how to utilize it with a few examples. I briefly introduce how the Routine works and when the code is assembled and executed. Furthermore, I present a few examples. I have used the Call Execute routine quite a few times before. Especially, in posts about performance, where I do several runs of the same code with varying parameters. You can see a few of them below
You can download the entire code from this post here.