Hello everyone,
I have tried to generate a double loop in a macro environment with %Do %until loops, so that I can execute the code dependent on two macro variables. (Larva.sas):
First lets explain a couple of programming things.
The condition in a %IF statement (and other macro logic statements) will already add an implied %EVAL() call. So you can save a lot of typing.
You pass the values of the parameters to the macro when you CALL the macro. You only need to add default values in the %MACRO statement if you want to make the macro easier to use by having default values for some parameters.
You want %DO %WHILE() not %DO %UNTIL() since you want the possibility of doing nothing.
%macro Larva(eggs,time_left,larva);
%put START &=larva &=eggs &=time_left;
%do %while(&eggs>0 and &time_left>=1200);
%let larva=%eval(&larva+1);
%let eggs=%eval(&eggs-1);
%let time_left=%eval(&time_left-1200);
%put &=larva &=eggs &=time_left;
%end;
%put FINISH &=larva &=eggs &=time_left;
%mend Larva;
Let's try it with your scenario:
460 %Larva 461 (Eggs = 6 462 ,time_left = 3600 463 ,Larva = 0 464 ); START LARVA=0 EGGS=6 TIME_LEFT=3600 LARVA=1 EGGS=5 TIME_LEFT=2400 LARVA=2 EGGS=4 TIME_LEFT=1200 LARVA=3 EGGS=3 TIME_LEFT=0 FINISH LARVA=3 EGGS=3 TIME_LEFT=0
Now if you want the new values of EGGS or LARVA (or TIME_LEFT) to be returned to the caller then you will need make some changes. Since they are the macro parameters they are by definition LOCAL macro variables. So they will disappear when the macro finishes running.
This is equivalent to
data hatchery;
larva = &eggs. * int(&time_left. / 1200);
call symputx("larva",larva);
run;
Since you do not reset &time_left to its original value, and use UNTIL, which forces at least one iteration to be done, it looks like this.
What do you want to achieve? Supply the rule, and show some examples for different values of eggs and time_left.
I do not understand what the DATA step is there for. You seem to be just changing the values of macro variables. If you do need a dataset then what is the macro logic for?
I also do not understand are you manually programming the logic to implement iterative DO loops.
Is this what you are trying to do?
%macro Larva(eggs,time_left);
%let larva=0;
%let start=&time_left;
%do eggs=&eggs %to 0 %by -1 ;
%do time_left=&start %to 0 %by -1200 ;
%let larva=%eval(&larva+1);
%put &=larva &=eggs &=time_left ;
%end;
%put %str();
%end;
%mend Larva;
Example run:
383 %Larva 384 (Eggs = 6 385 ,time_left = 3600 386 ); LARVA=1 EGGS=6 TIME_LEFT=3600 LARVA=2 EGGS=6 TIME_LEFT=2400 LARVA=3 EGGS=6 TIME_LEFT=1200 LARVA=4 EGGS=6 TIME_LEFT=0 LARVA=5 EGGS=5 TIME_LEFT=3600 LARVA=6 EGGS=5 TIME_LEFT=2400 LARVA=7 EGGS=5 TIME_LEFT=1200 LARVA=8 EGGS=5 TIME_LEFT=0 LARVA=9 EGGS=4 TIME_LEFT=3600 LARVA=10 EGGS=4 TIME_LEFT=2400 LARVA=11 EGGS=4 TIME_LEFT=1200 LARVA=12 EGGS=4 TIME_LEFT=0 LARVA=13 EGGS=3 TIME_LEFT=3600 LARVA=14 EGGS=3 TIME_LEFT=2400 LARVA=15 EGGS=3 TIME_LEFT=1200 LARVA=16 EGGS=3 TIME_LEFT=0 LARVA=17 EGGS=2 TIME_LEFT=3600 LARVA=18 EGGS=2 TIME_LEFT=2400 LARVA=19 EGGS=2 TIME_LEFT=1200 LARVA=20 EGGS=2 TIME_LEFT=0 LARVA=21 EGGS=1 TIME_LEFT=3600 LARVA=22 EGGS=1 TIME_LEFT=2400 LARVA=23 EGGS=1 TIME_LEFT=1200 LARVA=24 EGGS=1 TIME_LEFT=0 LARVA=25 EGGS=0 TIME_LEFT=3600 LARVA=26 EGGS=0 TIME_LEFT=2400 LARVA=27 EGGS=0 TIME_LEFT=1200 LARVA=28 EGGS=0 TIME_LEFT=0
Hi again @Tom ,
you are right, I do not need the data step for this macro. This was again just to test call symputx. The code should result in the generation of "larva" dependent on both conditions: Eggs should be greater than 0 and will be consumed per larva and Time for the transformation of Egg into Larva is taking 20 min. Hence if I have 6 Eggs but only 1 hour (3600 s) I should obtain 3 Larva with 3 Eggs remaining. I thought this would be a nice example to couple the two conditions using %do %until Loops:
%macro Larva ( Eggs = 6
,time_left = 3600
,Larva = 0
);
%if &Eggs. > 0 %then %do;
%do %until((%eval(&Eggs. < 1)) and (%eval(&time_left. < 1200)));
%let time_left = %eval(&time_left. - 1200);
%let Larva = %eval(&larva. +1);
%let Eggs = %eval(&Eggs. - 1);
%put &=Larva. &=Eggs. &=time_left.;
%end;
%end;
%mend Larva;
%larva()
But this results in:
LARVA=1 EGGS=5 TIME_LEFT=2400
LARVA=2 EGGS=4 TIME_LEFT=1200
LARVA=3 EGGS=3 TIME_LEFT=0
LARVA=4 EGGS=2 TIME_LEFT=-1200
LARVA=5 EGGS=1 TIME_LEFT=-2400
LARVA=6 EGGS=0 TIME_LEFT=-3600
Which is not the intent, since the time factor is being ignored. Hence I tried to execute the two loos sequentially but that lead to the same result.
First lets explain a couple of programming things.
The condition in a %IF statement (and other macro logic statements) will already add an implied %EVAL() call. So you can save a lot of typing.
You pass the values of the parameters to the macro when you CALL the macro. You only need to add default values in the %MACRO statement if you want to make the macro easier to use by having default values for some parameters.
You want %DO %WHILE() not %DO %UNTIL() since you want the possibility of doing nothing.
%macro Larva(eggs,time_left,larva);
%put START &=larva &=eggs &=time_left;
%do %while(&eggs>0 and &time_left>=1200);
%let larva=%eval(&larva+1);
%let eggs=%eval(&eggs-1);
%let time_left=%eval(&time_left-1200);
%put &=larva &=eggs &=time_left;
%end;
%put FINISH &=larva &=eggs &=time_left;
%mend Larva;
Let's try it with your scenario:
460 %Larva 461 (Eggs = 6 462 ,time_left = 3600 463 ,Larva = 0 464 ); START LARVA=0 EGGS=6 TIME_LEFT=3600 LARVA=1 EGGS=5 TIME_LEFT=2400 LARVA=2 EGGS=4 TIME_LEFT=1200 LARVA=3 EGGS=3 TIME_LEFT=0 FINISH LARVA=3 EGGS=3 TIME_LEFT=0
Now if you want the new values of EGGS or LARVA (or TIME_LEFT) to be returned to the caller then you will need make some changes. Since they are the macro parameters they are by definition LOCAL macro variables. So they will disappear when the macro finishes running.
Thank you @Tom ! Learned a lot today thanks to you. Especially the implied %EVAL is useful to know.
Looks like you want OR on the %DO %UNTIL, not AND:
%do %until((%eval(&Eggs. < 1)) or (%eval(&time_left. < 1200)));
You want to stop when you are out of eggs or out of time.
Thank you @Quentin ! I did not even think of this option, since in other languages the and operator meant that both conditions have to be fulfilled. But it makes sense here, I do it until one of the conditions is reached. Funny that it works the other way around with while, which also makes sense after thinking about it.
@ifb10 wrote:
Thank you @Quentin ! I did not even think of this option, since in other languages the and operator meant that both conditions have to be fulfilled. But it makes sense here, I do it until one of the conditions is reached. Funny that it works the other way around with while, which also makes sense after thinking about it.
Look up De Morgan's law.
Registration is now open for SAS Innovate 2025 , our biggest and most exciting global event of the year! Join us in Orlando, FL, May 6-9.
Sign up by Dec. 31 to get the 2024 rate of just $495.
Register now!
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.
Ready to level-up your skills? Choose your own adventure.