Почему Макропеременные SAS не являются локальными по умолчанию?


Я нашел эту очень полезную страницу SO, пытаясь решить проблему, связанную с областью видимости макросов. Почему %не позволяет создать локальную макросовую переменную?

Итак, подводя итог, написание %let x = [];или %do x = [] %to []; в макросе будет:

  • создайте локальную макропеременную x, если в глобальной таблице символов уже нет "x", или
  • обновите макропеременную глобального масштаба "x", Если "x" находится в глобальной таблице символов

Это поражает меня как очень не интуитивно понятный. Я был бы готов поспорить, что есть тонны ошибок в дикой природе SAS из-за этого выбора дизайна. Я редко вижу локальные операторы %в макросах, даже выше операторов цикла, использующих общие имена переменных, такие как" i "или" counter."Например, я только что вытащил первую статью со словом "макрос" в названии из этого списка документов SUGI и SAS Global Forum http://www.lexjansen.com/cgi-bin/xsl_transform.php?x=sgf2015&c=sugi

И действительно, я нашел этот код в первый документ конференции SAS, который я открыл:

%macro flag;
data CLAIMS;
 set CLAIMS;
 %do j= 1 %to 3;
 if icd9px&j in (&codelist)
 then _prostate=1;
 %end;
run;
%mend;
%flag;

Http://support.sas.com/resources/papers/proceedings15/1340-2015.pdf

Горе тому, кто называет % flag, а также имеет свою собственную переменную &j. Они могут легко закончить без ошибок журнала, но фальшивые результаты, потому что их &j-4 везде после вызова %flag, который будет (по опыту) ошибка, которую не интересно отслеживать. Или, что еще хуже, они могут никогда не признать свои результаты поддельными.

Итак, мой вопрос в том, почему это было было принято решение не включать все макропеременные в локальную область по умолчанию? Есть ли веские причины, по которым область видимости макросов SAS работает именно так?

3 5

3 ответа:

Во многом потому, что SAS-это 50-летний язык, который существовал до того, каклексическая область была явно предпочтительной.

SAS имеет смесь двух концепций области, но в основном динамически определяется, если вы намеренно не измените его. Это означает, что просто читая определение функции, вы не можете сказать, какие переменные будут доступны ей во время выполнения; и операторы присваивания применяются к версии переменной, которая в данный момент доступна во время выполнения (а не является принудительно находиться в самой локальной доступной области).

Это означает, что компилятор макросов не может определить, предназначена ли конкретная инструкция присваивания для назначения локальной макропеременности или, возможно, существующей во время выполнения макропеременности более высокой области. SAS может принудительно применять локальную макросовую переменную, как вы утверждаете, но это превратит SAS в лексический язык области, который не является желательным как на основе согласованности с прошлым (сохраняя обратную совместимость), так и на основе функциональности; предлагает возможность принудительно применять лексическую область (использовать %local), но не предлагает возможности намеренно изменять переменную в более высокой области (некоторая форма parent?) кроме %global.

Обратите внимание, что динамическая область была очень распространена еще в 60-е и 70-е годы. S-Plus, Lisp и т. д. все они имели динамический охват. SAS, как правило, предпочитает обратную совместимость, насколько это возможно. SAS также обычно используется аналитиками, а не программистами, и поэтому необходимо избегать сложности, когда это возможно. Они предложение %local для тех из нас, кто действительно хочет воспользоваться преимуществами лексической области

Ответить, почему правила определения области были определены таким образом, мне трудно, не зная истории макроязыка.

Когда я изучал макроязык (на 6.12), мне повезло, что меня с самого начала учили, что макросы всегда должны объявлять свои переменные как %LOCAL, если только у них не было действительно веской причины этого не делать. Иногда, если макрос var не был объявлен как %local или %global, я даже помещал в них комментарий /* Not Local: MyMacVar */, чтобы документально подтвердить, что я не собирался объявлять область действия (это необычно, но иногда полезно). Мне больно видеть UG документы, SO ответы и т. д., которые не объявляют переменные как % LOCAL.

Я собираюсь предположить (это всего лишь предположение), что была какая-то ранняя версия SAS, которая имела (глобальные) макропеременные для генерации текста в коде, но не имела макросов. Таким образом, в такой версии люди привыкли бы иметь множество глобальных макропеременных и связанных с ними проблем (например, столкновений). Затем, когда SAS разработал макросы, возник бы вопрос: "Могу ли я ссылаться на мои макро-Вары изнутри макроса?"И дизайнер решил ответить:" Да, вы можете не только ссылаться на них, но и присваивать им значения, и я облегчу это, разрешив вам делать это по умолчанию. Кроме того, макрос создает свою собственную область видимости, которая может содержать локальные переменные макроса. Если вы ссылаетесь на макрос var или назначаете макрос var с тем же именем, что и макрос var, существующий в глобальной области (или любой внешней области), я буду считать, что вы ссылка на глобальную переменную макроса (как вы уже привыкли), если только вы явно не объявили макрос var как %LOCAL."

С точки зрения современного макроязыка / разработчика макросов, большинство людей считают, что следует избегать большинства глобальных макро-Варс. И одно из преимуществ макроязыка состоит в том, что он предоставляет макросы, которые позволяют модуляризировать/инкапсулировать/скрывать информацию. При рассмотрении с этой точки зрения, %локальные переменные более полезны, и макрос переменные, которые не объявлены как %local, представляют собой угрозу инкапсуляции (т. е. угрозу столкновения). Поэтому я склонен согласиться с тем, что если бы я перепроектировал макроязык, то по умолчанию сделал бы макропеременные %локальными. Но, конечно, сейчас уже слишком поздно что-то менять.

Тогда мы не могли бы сделать этого или, по крайней мере, без нового декларативного заявления.

33         %let c=C is global;
34         %macro b(arg);
35            %let &arg=Set by B;
36            %mend b;
37         %macro a(arg);
38            %local c;
39            %b(c);
40            %put NOTE: &=c;
41            %mend a;
42         %a();
NOTE: C=Set by B