BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
gamotte
Rhodochrosite | Level 12

Hello,

 

I have a hard time understanding how the call vnext routine works. Can someone explain what is the logic

behind the output below ?

 

619
620
621  data _NULL_;
622    set sashelp.class;
623    length _VNAME_ $32.;
624
625    call vnext(_VNAME_);
626    put _N_ _VNAME_=;
627
628    call vnext(_VNAME_);
629    put _N_ _VNAME_=;
630
631    call vnext(_VNAME_);
632    put _N_ _VNAME_=;
633
634  run;

1 _VNAME_=Name
1 _VNAME_=Name
1 _VNAME_=Name
2 _VNAME_=Name
2 _VNAME_=Sex
2 _VNAME_=Sex
3 _VNAME_=Name
3 _VNAME_=Age
3 _VNAME_=Age
4 _VNAME_=Name
4 _VNAME_=Height
4 _VNAME_=Height
5 _VNAME_=Name
5 _VNAME_=Weight
5 _VNAME_=Weight
6 _VNAME_=Name
6 _VNAME_=_VNAME_
6 _VNAME_=_VNAME_
7 _VNAME_=Name
7 _VNAME_=_ERROR_
7 _VNAME_=_ERROR_
8 _VNAME_=Name
8 _VNAME_=_N_
8 _VNAME_=_N_
9 _VNAME_=Name
9 _VNAME_=
9 _VNAME_=Name
10 _VNAME_=Name
10 _VNAME_=Name
10 _VNAME_=Sex
11 _VNAME_=Name
11 _VNAME_=Sex
11 _VNAME_=Age
12 _VNAME_=Name
12 _VNAME_=Age
12 _VNAME_=Height
13 _VNAME_=Name
13 _VNAME_=Height
13 _VNAME_=Weight
14 _VNAME_=Name
14 _VNAME_=Weight
14 _VNAME_=_VNAME_
15 _VNAME_=Name
15 _VNAME_=_VNAME_
15 _VNAME_=_ERROR_
16 _VNAME_=Name
16 _VNAME_=_ERROR_
16 _VNAME_=_N_
17 _VNAME_=Name
17 _VNAME_=_N_
17 _VNAME_=
18 _VNAME_=Name
18 _VNAME_=
18 _VNAME_=Name
19 _VNAME_=Name
19 _VNAME_=Name
19 _VNAME_=Sex
NOTE: There were 19 observations read from the data set SASHELP.CLASS.
NOTE: DATA statement used (Total process time):
      real time           0.00 seconds
      cpu time            0.00 seconds

Thanks.

1 ACCEPTED SOLUTION

Accepted Solutions
FreelanceReinh
Jade | Level 19

Hello @gamotte,

 

Like @novinosrin this behavior of CALL VNEXT reminded me of the LAG function in that each occurrence of CALL VNEXT in the code seems to produce a separate "stream" of variable names (assumption 1). In addition, it seems that the routine returns a missing value after the last variable name and then starts another cycle with the first variable name (reminding me of the NEXT method of the hash iterator object) -- assumption 2 (edit: The first part of this assumption is also stated in the documentation). Together with these assumptions the two rules stated in the documentation (call them rule 1 and rule 2) explain the observed values (let's refer to the k-th row of the j-th _N_ value, i.e. j=_N_, k=1, 2, 3, as "j.k"):

  • _VNAME_=Name in rows j.1, j=1, ..., 19, due to rule 1, because variable _VNAME_ is not retained, hence missing at the beginning of each DATA step iteration.
  • In rows j.2, j=1, ..., 8, and j.3, j=1, ..., 8, the second and third CALL-VNEXT calls cycle through the variable list "Name, Sex, ..., _ERROR_, _N_" independently, according to assumption 1 and rule 2.
  • _VNAME_ is blank in row 9.2 according to assumption 2, which leads to _VNAME_=Name in row 9.3 by virtue of rule 1.
  • _VNAME_=Name in row 10.2 starts the second cycle according to assumption 2, while row 10.3 is now "one step ahead" because the third CALL VNEXT processed "Name" already in the previous call (row 9.3).
  • The two independent cycles continue until the third CALL VNEXT has exhausted the variable list again in row 17.3 and therefore returns a missing value (but _VNAME_ would be missing before the next call of the first CALL VNEXT anyway, hence nothing special in row 18.1).
  • One call later than the third CALL VNEXT also the second CALL VNEXT has exhausted the variable list and returns a missing value in row 18.2, which enforces _VNAME_=Name in row 18.3 (but starting the next cycle according to assumption 2 would have the same result).
  • In rows 19.2 and 19.3 the second and third CALL VNEXT start/continue their third cycles (as they did their second cycles in rows 10.2 and 10.3, respectively).

 

View solution in original post

6 REPLIES 6
novinosrin
Tourmaline | Level 20

HI @gamotte  My guess is this logic is akin to how a lag function works-  LAG1 seems fixed and is called for the same allocated queue, the item in the front of the queue is ejected (called dequeueing), and the value of the argument enters the rear of the queue (called enqueueing), 

 

x = lag (x) ;

x = lag (x) ;

 

is not  the same as:

 

do i = 1 to 2 ;

  x = lag (x) ;

end ;

 

Disclaimer: It's a guess. I could be wrong!

 

novinosrin
Tourmaline | Level 20

Another guess:

 

1. Everytime a Call Vnext routine is executed, it probably increments an index variable by 1

2. The likelihood is that the variables are stored in a key indexed sequence. 

3. The index variable ought to be automatic for processing and dropping

4. So for each independent Call Vnext routine, there's a unique index variable that is perhaps created

FreelanceReinh
Jade | Level 19

Hello @gamotte,

 

Like @novinosrin this behavior of CALL VNEXT reminded me of the LAG function in that each occurrence of CALL VNEXT in the code seems to produce a separate "stream" of variable names (assumption 1). In addition, it seems that the routine returns a missing value after the last variable name and then starts another cycle with the first variable name (reminding me of the NEXT method of the hash iterator object) -- assumption 2 (edit: The first part of this assumption is also stated in the documentation). Together with these assumptions the two rules stated in the documentation (call them rule 1 and rule 2) explain the observed values (let's refer to the k-th row of the j-th _N_ value, i.e. j=_N_, k=1, 2, 3, as "j.k"):

  • _VNAME_=Name in rows j.1, j=1, ..., 19, due to rule 1, because variable _VNAME_ is not retained, hence missing at the beginning of each DATA step iteration.
  • In rows j.2, j=1, ..., 8, and j.3, j=1, ..., 8, the second and third CALL-VNEXT calls cycle through the variable list "Name, Sex, ..., _ERROR_, _N_" independently, according to assumption 1 and rule 2.
  • _VNAME_ is blank in row 9.2 according to assumption 2, which leads to _VNAME_=Name in row 9.3 by virtue of rule 1.
  • _VNAME_=Name in row 10.2 starts the second cycle according to assumption 2, while row 10.3 is now "one step ahead" because the third CALL VNEXT processed "Name" already in the previous call (row 9.3).
  • The two independent cycles continue until the third CALL VNEXT has exhausted the variable list again in row 17.3 and therefore returns a missing value (but _VNAME_ would be missing before the next call of the first CALL VNEXT anyway, hence nothing special in row 18.1).
  • One call later than the third CALL VNEXT also the second CALL VNEXT has exhausted the variable list and returns a missing value in row 18.2, which enforces _VNAME_=Name in row 18.3 (but starting the next cycle according to assumption 2 would have the same result).
  • In rows 19.2 and 19.3 the second and third CALL VNEXT start/continue their third cycles (as they did their second cycles in rows 10.2 and 10.3, respectively).

 

ballardw
Super User

Here's some evidence related to a "queue per variable used" theory

176  data _NULL_;
177    set sashelp.class;
178    length _VNAME1 - _VNAME3 $32.;
179
180    call vnext(_VNAME1);
181    put _N_ _VNAME1=;
182
183    call vnext(_VNAME2);
184    put _N_ _VNAME2=;
185
186    call vnext(_VNAME3);
187    put _N_ _VNAME3=;
188
189  run;

1 _VNAME1=Name
1 _VNAME2=Name
1 _VNAME3=Name
2 _VNAME1=Name
2 _VNAME2=Name
2 _VNAME3=Name
3 _VNAME1=Name
3 _VNAME2=Name
3 _VNAME3=Name
4 _VNAME1=Name
4 _VNAME2=Name
4 _VNAME3=Name
5 _VNAME1=Name
5 _VNAME2=Name
5 _VNAME3=Name
6 _VNAME1=Name
6 _VNAME2=Name
6 _VNAME3=Name
7 _VNAME1=Name
7 _VNAME2=Name
7 _VNAME3=Name
8 _VNAME1=Name
8 _VNAME2=Name
8 _VNAME3=Name
9 _VNAME1=Name
9 _VNAME2=Name
9 _VNAME3=Name
10 _VNAME1=Name
10 _VNAME2=Name
10 _VNAME3=Name
11 _VNAME1=Name
11 _VNAME2=Name
11 _VNAME3=Name
12 _VNAME1=Name
12 _VNAME2=Name
12 _VNAME3=Name
13 _VNAME1=Name
13 _VNAME2=Name
13 _VNAME3=Name
14 _VNAME1=Name
14 _VNAME2=Name
14 _VNAME3=Name
15 _VNAME1=Name
15 _VNAME2=Name
15 _VNAME3=Name
16 _VNAME1=Name
16 _VNAME2=Name
16 _VNAME3=Name
17 _VNAME1=Name
17 _VNAME2=Name
17 _VNAME3=Name
18 _VNAME1=Name
18 _VNAME2=Name
18 _VNAME3=Name
19 _VNAME1=Name
19 _VNAME2=Name
19 _VNAME3=Name
NOTE: There were 19 observations read from the data set SASHELP.CLASS.
NOTE: DATA statement used (Total process time):
      real time           0.00 seconds
      cpu time            0.00 seconds

FreelanceReinh
Jade | Level 19

@ballardw: I think with a RETAIN _VNAME1-_VNAME3; statement added, the "evidence" will be more convincing because otherwise "rule 1" alone would already explain the result.

gamotte
Rhodochrosite | Level 12

Thanks a lot @novinosrin and @FreelanceReinh for these insightful answers. It makes much more sense now.

SAS Innovate 2025: Call for Content

Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!

Submit your idea!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 6 replies
  • 880 views
  • 4 likes
  • 4 in conversation