The correct code is as follows. But I used almost an hour to figure it out and still have questions about it. (ps: I made these mistakes while doing the practice questions on page 285 in Macro1: essentials course notes pdf, that was a complex question.)
1) The correct code and results
%macro ctrylist(ctry1)/minoperator;
%let list1=AT AU CA CH;
%put &list1;
%if &ctry1 in &list1 %then %do;
%put &ctry1;
%end;
%else %do;
%put ERROR: &ctry1 is an invalid country code.;
%put ERROR: Valid country codes include &list1.;
%end;
%mend ctrylist;
%ctrylist(AU);
%ctrylist(zz);
2) How I used almost an hour to make mistakes and correct it. At first I wrote something like this (I did not know what's wrong with my mind then and forgot to write the semicolon ; behind the two %put statements in the %else %do statement, also when debugging I kept ignore this obvious mistake and costed me a lot of time to figure out the problem):
%macro ctrylist(ctry1);
%let list1=AT AU CA CH;
%put &list1;
%if &ctry1 in &list1 %then %do;
%put &ctry1;
%end;
%else %do;
%put ERROR: &ctry1 is an invalid country code.
%put ERROR: Valid country codes include &list1.
%end;
%mend ctrylist;
%ctrylist(AU);
%ctrylist(zz);
And I got error message like this:
I am not good at using error message to help debug, also did not pay attention to where did the error occur. The possible mistakes came into my mind were: 1) maybe the %if &ctry1 in &list1 statement has problem, maybe I should try a &list with commas that separate the values, and 2) maybe the first %put in the first %if %then statement is not necessary. Then I tried something like this:
%macro ctrylist2(ctry2);
%let list2='AT','AU','CA','CH';
%if &ctry2 in (&list2) %then %do;
&ctry2;
%end;
%else %do;
%put ERROR: &ctry2 is an invalid country code.
%put ERROR: Valid country codes include &list2.
%end;
%mend ctrylist2;
%ctrylist(AU);
%ctrylist(zz);
And I got the same error message:
Then I did not know what to do. The only thing came into my mind was, maybe an option minoperator should be added (because in that section in the material there was content of minoperator), then I tried something like this:
%macro ctrylist2(ctry2)/minoperator; %let list2='AT','AU','CA','CH'; %if &ctry2 in (&list2) %then %do; %put &ctry2; %end; %else %do; %put ERROR: &ctry2 is an invalid country code. %put ERROR: Valid country codes include &list2. %end; %mend ctrylist2; %ctrylist(AU); %ctrylist(zz);
And I got the exact same error message. I did not know why I kept ignore the obvious mistake that there was no semicolon ; after the two %put statements, I even did not put my eyesight at the two statements, like I kept ignore an obvious, major mistake intentionally. Then I think I cannot figure it out and was going to post the problem into this community. But right before I post the problem my attention and eyesight somehow switched to and see the mistake: I did not write semicolon ; after the two statements. And I corrected it.
3) What I learnt from the experience: a) do not in a hurry and too eager to find out what is wrong, just look into every details of the code, b)look at the error message, especially where does the error message occur, c) when writing complex macros, keep relax and keep attention focused and enjoy, do not rush. d) When writing macro, it is easy for me to forget the semicolon, or sometimes added unnecessary semicolon or put semicolon at wrong place, I made this mistake twice before, the scenario is like(I forget another example which use a %if %then and where statement in a data step, which has the similar usage of the semicolon, could anyone come up with a similar example, many thanks) this, for this incorrect code I should put the semicolon after the &&b&i right before run; statement and after the %end; statement:
data a;
set %do i=1 to &sqlobs;
&&b&i;
%end;
run;
4) I still have questions about the codes:
a) do %if &ctry1 in &list1 and %if &ctry2 in (&list2) work the same way? I mean the syntax of the %if in statement, the first one does not need the brackets and the second one does need the brackets, isn't it?
%let list1=AT AU CA CH;
%put &list1;
%if &ctry1 in &list1 %then %do;
%put &ctry1;
%end;
%let list2='AT','AU','CA','CH';
%if &ctry2 in (&list2) %then %do;
%put &ctry2;
%end;
b) what does the minoperator options do? why do I need it after the macro name? Without it I got a similar error message (only without the error message about %put).
%macro ctrylist2(ctry2);
%let list2='AT','AU','CA','CH';
%if &ctry2 in (&list2) %then %do;
%put &ctry2;
%end;
%else %do;
%put ERROR: &ctry2 is an invalid country code.;
%put ERROR: Valid country codes include &list2.;
%end;
%mend ctrylist2;
%ctrylist(AU);
%ctrylist(zz);
... View more