Chapter 8: %LOCAL vs. %GLOBAL

8.1 Why Think %Locally?

8.2 Creating Symbol Tables and Macro Variables

8.2.1 Programming Challenge #6

8.2.2 Solution

8.3 Symbol Tables with CALL SYMPUT

8.4 Symbol Tables with CALL SYMPUTX

8.5 Choosing the Source Table

8.6 A Persisting Impact

Global variables. Local variables that automatically vanish. A compendium of rules that depend on which tool creates a macro variable. Why do we need these complications?

8.1 Why Think %Locally?

Why should this be an issue? Why not just make all macro variables global, and eliminate the related complications? After all, in a %GLOBAL world, %LET would have the same effect whether it appears inside or outside of a macro. In this example, %UPPER affects the one (and only) %GLOBAL version of &SIZES2:

%let sizes1 = Small Medium Large;

%let sizes2 = Small Medium Large;

%let sizes1 = %upcase(&sizes1);

%macro upper;

     %let sizes2 = %upcase(&sizes2);

%mend upper;

%upper

%put &sizes1;     SMALL MEDIUM LARGE

%put &sizes2;     SMALL MEDIUM LARGE

But when local variables exist, the result changes:

%let sizes1 = Small Medium Large;

%let sizes2 = Small Medium Large;

%let sizes1 = %upcase(&sizes1);

%macro upper;

     %local sizes2;

     %let sizes2 = %upcase(&sizes2);

%mend upper;

%upper

%put &sizes1;     SMALL MEDIUM LARGE

%put &sizes2;     Small Medium Large

Within %UPPER, the %UPCASE function works on the local version of &SIZES2. But the %PUT statement works on the global version. The local version disappears as soon as %UPPER finishes executing.

So what are the benefits of this added complexity? The first benefit has already been mentioned in passing. When a macro finishes executing, cleanup occurs automatically. Its symbol table vanishes. Removing a %GLOBAL macro variable actually takes more work:

%symdel sizes1;

But the primary advantage of creating %LOCAL symbol tables is that many programmers can write many macros independently of one another. Programmers need not worry about which macro variable names are already in use by other macros because changes to variables in one local symbol table do not affect any other symbol tables.

8.2 Creating Symbol Tables and Macro Variables

When does the software create a local symbol table? Not until it is absolutely necessary. None of these conditions are sufficient:

•    Defining a macro

•    Executing a macro that works with only global variables

•    Executing CALL SYMPUT.

This test sheds further light on the subject:

%macro test;

     %put _local_;

     %local abc;

     %put _local_;

%mend test;

%test

Only the second %PUT statement writes any messages. The local table does not exist until the %LOCAL statement executes.

When macros call macros, multiple symbol tables exist, and complications arise. How can you create a macro variable in the proper table? When retrieving a macro variable, how do you retrieve the proper variable when two symbol tables both contain a macro variable having the same name?

To navigate the complications, master a few basic principles:

•    The effects of a %GLOBAL statement

•    The effects of a %LOCAL statement

•    The search process that the software employs to locate an existing macro variable.

%global abc;

This statement means, “If the global symbol table contains a macro variable &ABC, do nothing. But if not, create &ABC in the global symbol table, assigning it a null value.” Suppose these two statements were to appear within a macro definition:

%global abc;

%let abc=;

Is the second statement really needed? Won’t the %GLOBAL statement create &ABC with a null value? Actually, the second statement might be vital. If the %GLOBAL statement creates &ABC, the %LET statement is not needed. However, the %GLOBAL statement does nothing if &ABC already exists in the global symbol table. In fact, &ABC might already exist if a program calls the same macro twice. For the second execution, the %LET statement might be necessary to reset &ABC to a null value.

%local abc;

This statement means, “If the local symbol table (for the macro that contains this statement) contains &ABC, do nothing. But if not, create &ABC in that local symbol table, assigning it a null value.” %LOCAL statements can appear only within a macro definition.

The only function of the %LOCAL and %GLOBAL statements is to create a macro variable if it does not already exist. In that light, this program is perfectly valid:

%macro my_way;

   %global abc;

   %local abc;

%mend my_way;

The macro creates &ABC in the global symbol table, and it then creates another macro variable (also named &ABC) in the local symbol table. But what if we were to reverse the order?

%macro your_ways;

     %local abc;

     %global abc;

%mend your_ways;

Can you see the error in %YOUR_WAYS? Perhaps this code should run error-free, but it doesn’t. Evidently, this combination fears that you are misunderstanding how the %GLOBAL statement works, and it thinks you are attempting to move a local variable to the global table. So to prevent this misunderstanding from generating an incorrect result, this code generates an error instead. Don’t attempt to create a global variable when the local symbol table already contains a macro variable with the same name. Subterfuge will fail. This program generates the same error:

%macro a;

     %local abc;

     %b

%mend a;

%macro b;

     %global abc;

%mend b;

%a

The final basic principle is the search process. What happens if a macro refers to &ABC, and both the local and global symbol tables contain &ABC? The program retrieves the local version. The software searches for &ABC in the local table first and then in the global table. It uses the first &ABC it finds. (In the case of macros calling macros, the search order begins with the innermost local table and ends with the global table.)

8.2.1 Programming Challenge #6

With these principles in mind, find the fundamental flaw in this macro:

%macro sequels (movie);

     %do m=1 %to 5;

         proc print data=all.movies;

            where movie = "&movie &m";

            title "Movie:  &movie &m";

         run;

     %end;

%mend sequels;

Clearly, some values of &MOVIE will have fewer than four sequels. But no harm is done by a WHERE statement that retrieves zero observations. So where is the flaw?

8.2.2 Solution

Even though the code runs error-free, it still embodies a fundamental flaw. The macro is missing a %LOCAL statement, creating &M in its local symbol table. Consider the consequences if another macro were to call this macro. If the calling macro contains a macro variable &M, even if it were defined with a %LOCAL statement within the calling macro, %SEQUELS would change &M in the calling macro’s symbol table. Omitting the %LOCAL statement within %SEQUELS makes it unsafe for another macro to call the %SEQUELS macro. For example, consider this relatively simple macro that harbors the same flaw:

%macro varlist;

   %do m=1 %to 3;

      var&m

   %end;

%mend varlist;

Try pairing such a simple macro with a variation of %SEQUELS:

%macro sequels (movie);

   %do m=1 %to 5;

       proc print data=all.movies;

          where movie = "&movie &m";

          title "Movie:  &movie &m";

          var %varlist;

       run;

   %end;

%mend sequels;

Now executing %SEQUELS generates an infinite loop. %VARLIST utilizes &M from the local symbol table of %SEQUELS. Thus this loop never finishes:

%do m=1 %to 5;

Don’t write dangerous macros. Define your %LOCAL variables!

8.3 Symbol Tables with CALL SYMPUT

CALL SYMPUT complicates matters when it creates a macro variable. It follows two rules to determine whether a macro variable needs to be created and which symbol table to use:

•    Search for an existing macro variable that has the proper name. If it exists, use the existing variable.

•    If the target macro variable does not exist, create it but do not create a symbol table. Instead, place it in the closest existing symbol table.

This program illustrates those rules:

%macro where_did_it_go;

     data _null_;

        call symput("before", "??");

     run;

     %local abc;

     data _null_;

        call symput("after", "????");

     run;

     %put _user_;

%mend where_did_it_go;

%where_did_it_go

The first CALL SYMPUT writes &BEFORE to the global table because the local table does not exist yet. Next, executing the %LOCAL statement forces creation of the local symbol table. The second CALL SYMPUT writes &AFTER to the suddenly available local symbol table.

Many macros avoid this complication automatically:

%macro typical (dsn=, n=);

Once a macro begins to execute, parameters on the %MACRO statement force creation of a local symbol table. If an embedded CALL SYMPUT must create a macro variable, a local symbol table is available.

Next, examine a deceptively simple macro. Which symbol table will CALL SYMPUT use when creating &VAR1 through &VAR5?

%macro it_depends;

   %do w=1 %to 5;

       data _null_;

          call symput("var&w", "&w");

       run;

   %end;

%mend it_depends;

%it_depends

The answer is, “It depends.” Sometimes CALL SYMPUT uses the global table, and sometimes it uses the local table. How can that be?

As the macro begins to execute, there is no local symbol table. The key question becomes: Does the global symbol table already contain &W? If it does:

•    The %DO loop iterates the global variable.

•    There is no local symbol table.

•    CALL SYMPUT must use the global symbol table.

But if the global symbol table does not already contain &W:

•    The %DO loop is forced to create &W, creating a local symbol table to hold it.

•    CALL SYMPUT uses the local symbol table.

Even though the complete macro definition is in plain sight, there is no way to know ahead of time which symbol table CALL SYMPUT will use.

One last programming feature affects the creation of a local symbol table. Section 6.5 noted how PROC SQL automatically creates macro variables. As part of that process, PROC SQL always uses the local symbol table; if that table does not yet exist, the presence of PROC SQL forces the software to create it. And as this program illustrates, creation of that local table affects CALL SYMPUT’s macro variables:

%macro which_table;

   data _null_;

      call symput('before', 'before SQL'),   to global table

   run;

   proc sql noprint;

      create table new as select * from old;

   quit;

   data _null_;

      call symput ('after', 'after SQL'),    to local table

   run;

   %put _user_;

%mend which_table;

%which_table

The first CALL SYMPUT writes to the global table because there is no local table. When PROC SQL runs, the software creates a local symbol table. Therefore, the second CALL SYMPUT writes to the suddenly available local table.

8.4 Symbol Tables with CALL SYMPUTX

Section 5.5 introduced CALL SYMPUTX, the expanded version of CALL SYMPUT. This section revisits CALL SYMPUTX, focusing on its ability to control the symbol table that holds its output.

CALL SYMPUTX supports a third parameter:

call symputx("varname", "value", F/G/L);

When the third parameter is F, the software tries to Find an existing macro variable with the proper name. This is the default action, the same behavior that CALL SYMPUT uses. When the third parameter is G, there is no search for an existing macro variable. The software automatically stores &VARNAME in the Global symbol table. When the third parameter is L, the software uses the closest available Local symbol table. Again, the software skips any attempt to locate &VARNAME in an existing symbol table.

8.5 Choosing the Source Table

With a system of macros calling macros calling macros, suppose you would like to retrieve &P from the global symbol table. There may or may not be other &P values in various local symbol tables. How do you retrieve &P from the global symbol table, regardless of the contents of any local symbol tables?

The first step is to check to see whether &P exists. If so, where does it exist? Any of these statements can help:

%if %symglobl(P) %then %do;

%if %symlocal(P) %then %do;

%if %symexist(P) %then %do;

These statements check respectively whether &P exists in the global symbol table, whether it exists in a local symbol table, and whether it exists at all. All three functions return a 1 when the variable exists and a 0 when it doesn't.

If &P exists in the global symbol table, its value is available ... even if another &P exists in a local symbol table. But retrieving it takes more work.

The SASHELP.VMACRO data set tracks all existing macro variables in all symbol tables, and that mapping is available to both the DATA step and PROC SQL. NAME, SCOPE, and VALUE are all stored for each macro variable. Here is some code that could appear within a macro:

%local global_version_of_p;

data _null_;

   set sashelp.vmacro;

   where name='P' and scope='GLOBAL';

   call symputx('global_version_of_p', value);

run;

Notice a few details. First, VALUE has a length of 200. For shorter macro variables that contain trailing blanks, SASHELP.VMACRO does not track how many trailing blanks were in the original value. Also note that CALL SYMPUTX removes all leading and trailing blanks. Finally, what if the original macro variable were longer than 200 characters? In that case, its value gets split into 200-character chunks, and it occupies multiple observations in SASHELP.VMACRO. The variable OFFSET tracks how to reassemble the pieces:

Scope

Name

Offset

Value

GLOBAL

P

0

First 200 chars of P in Global Symbol Table

GLOBAL

P

200

Chars 201-400 of P in Global Symbol Table

GLOBAL

P

400

Chars 401-600 of P in Global Symbol Table

GLOBAL

P

600

Chars 601-800 of P in Global Symbol Table

When OFFSET is 0, VALUE contains the first 200 characters of a macro variable. When OFFSET is 200, VALUE contains the next 200 characters. Think of OFFSET as the number of characters to move to the right, before placing VALUE into a portion of a macro variable. While a DATA step could reassemble the pieces, PROC SQL makes it look (relatively) easy:

proc sql noprint;

      select value into : global_version_of_p separated by ''

      from sashelp.vmacro

      where name='P' and scope='GLOBAL';

quit;

Notice how SEPARATED BY uses a null character, not a blank. As shown in Section 6.10, a null separator strings all the VALUEs together without inserting any blanks between them.

8.6 A Persisting Impact

When a macro finishes executing, its symbol table vanishes. What if the program needs some of that information? What tools or techniques allow a macro to contribute to a program, even after the macro completes?

The simplest technique is to create global macro variables. Both a %GLOBAL statement and CALL SYMPUTX play a role here. A second approach is to generate text instead of saving macro variables. If a macro has generated some text, those words remain part of the program after the macro ends. Chapter 10 will explore this approach in greater detail. For now, we will examine a different technique: passing the name (rather than the value) of a global macro variable as a macro parameter.

Here is the background for this example. A macro adds "Jack" at the beginning of several macro variables. Before the macro begins, these global variables exist:

%let var1 = be nimble;

%let var5 = be quick;

%let var8 = jump over the candlestick;

The straightforward approach skips defining a macro:

%let var1 = Jack &var1;   Jack be nimble

%let var5 = Jack &var2;   Jack be quick

%let var8 = Jack &var3;   Jack jump over the candlestick

Clearly, this is the right approach for such a simple objective. But this is just a simplified example, in order to illustrate a point. If a macro were to approach the same problem, it might encounter difficulties:

%macro add_jack (firstvar=, secondvar=, thirdvar=);

   %let firstvar      = Jack &firstvar;

   %let secondvar  = Jack &secondvar;

   %let thirdvar     = Jack &thirdvar;

%mend add_jack;

%add_jack (firstvar=&var1, secondvar=&var5, thirdvar=&var8)

While the macro can add "Jack" in the right places, it has difficulty replacing &VAR1, &VAR5, and &VAR8 in the global symbol table. Instead, it replaces &FIRSTVAR, &SECONDVAR, and &THIRDVAR in the local symbol table. A better approach would pass the names of the global macro variables rather than their values. Here is one possibility:

%macro add_jack (firstvar=, secondvar=, thirdvar=);

   %let &firstvar      = Jack &&&firstvar;

   %let &secondvar = Jack &&&secondvar;

   %let &thirdvar    = Jack &&&thirdvar;

%mend add_jack;

%add_jack (firstvar=var1, secondvar=var5, thirdvar=var8)

Of course, this approach adds complexities such as using three ampersands. But it successfully generates %LET statements that change existing global macro variables. And this key technique makes it work: passing the name of a global macro variable.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.21.152