Цитирование макроса SAS: передать знак равенства в качестве аргумента макроса

Я пишу макрос, который в какой-то момент вызывает некоторый код процедуры SQL. Я хочу, чтобы пользователь мог указывать произвольные параметры sql процедуры (например, inobs = 100 может быть одним из входных аргументов моего макроса).

Мне очень трудно цитировать аргумент, который имеет знак равенства '='.

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

Ниже приведен пример неработающего теста, который не работает и выдает ошибку

ОШИБКА: параметр ключевого слова INOBS не был определен с макросом.

Я прочитал это (http://www2.sas.com/proceedings/sugi28/011-28.pdf) и другие SUGI и попробовали много возможных способов цитировать и вызывать макрос.

Был бы очень признателен, если бы кто-нибудь мог предоставить рабочий пример нижеприведенной функции.

options mprint mlogic;

data have;
    length x $8;
    input x;
    datalines;
one
two
three
;

proc sql inobs=2;
    create table sql_output as 
            select * 
            from have;
quit;


%macro pass_parameter_with_equal_sign(table=, sqlOptions=);
    proc sql 
%if "%left(%trim(&sqlOptions.))" ne "" %then %do;
    &sqlOptions.
%end;
    /* the semicolon to end the proc sql statement */
    ;
        create table macro_output as 
            select * 
            from have;
    quit;
%mend;

%pass_parameter_with_equal_sign(table=have, sqlOptions=%str(inobs=2))

title "SQL output:";
proc print data=sql_output; run;
title "Macro output:";
proc print data=macro_output; run;

person jaamor    schedule 24.11.2015    source источник
comment
В качестве примечания ... Нет необходимости заключать макрос в кавычки со знаком равенства. SAS достаточно умен, чтобы знать, что знак равенства - это просто часть значения параметра. Однако вам придется заключить запятые в кавычки, так как они ограничивают параметры макроса.   -  person Robert Penridge    schedule 25.11.2015


Ответы (3)


Ах, это была небольшая хитрая проблема, с которой ты столкнулся. На самом деле проблема была вызвана вызовами %trim() и %left().

Удаление этих результатов в коде, который работает по назначению (обратите внимание, что я также удалил макрос, цитирующий параметр):

%macro pass_parameter_with_equal_sign(table=, sqlOptions=);
    proc sql 
    %if "&sqlOptions" ne "" %then %do;
        &sqlOptions
    %end;
    /* the semicolon to end the proc sql statement */
    ;
        create table macro_output as 
            select * 
            from &table;
    quit;
%mend;

%pass_parameter_with_equal_sign(table=sashelp.class, sqlOptions= inobs=2);

Мы можем воссоздать проблему, с которой вы столкнулись, следующим образом:

%put %trim(inobs=1);

Поскольку параметр разрешался в inobs = 1, а %trim() не имеет никаких именованных параметров, это шипело. Чтобы правильно передать строку, содержащую inobs = 1, мы можем сделать это следующим образом:

%let param = inobs=1;
%put %trim(%str(&param));

Примечание. Решение Амира полностью удалить оператор %if также является лучшим способом создания подобного кода. Я просто поясняю, почему у вас возникла эта проблема.


Дополнительное объяснение 1. Почему %left() и %trim не нужны

Верхний фрагмент кода обеспечивает те же функциональные возможности, что и исходный код с "%left(%trim(&sqlOptions.))". Это связано с тем, что начальный и конечный пробелы удаляются из макропеременных (включая параметры макрокоманды), если они явно не сохраняются с помощью кавычек макроса. Простой пример, демонстрирующий это:

%let param =      lots      of     spaces        ;
%put ***&param***;

Дает:

***lots      of     spaces***

Вы можете видеть, что внутренние пробелы сохранены, но пропало левое и правое отступы. Чтобы сохранить пробелы, мы можем просто использовать функцию %str().

%let param = %str(     lots      of     spaces        );
%put ***&param***;

Дает:

***     lots      of     spaces        ***

Дополнительное объяснение 2. Работа с макросами, содержащими пробелы

Если у вас действительно были пробелы в макропеременной, которые вам нужно было удалить, потому что они были заключены в кавычки, и вы хотели использовать для этого %left() и %trim(), тогда все становится немного странно. Наша переменная может быть создана так:

%let param = %str(     inobs = 2        );

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

%put %trim(&param);  * ALREADY QUOTED AT CREATION SO THIS WORKS FINE;

Однако, если мы затем попытаемся передать результат в функцию %left(), мы вернемся к исходной проблеме:

%put %left(%trim(&param));  * OOPS. DOESNT WORK;

Теперь я предполагаю здесь, но я считаю, что это наиболее вероятно, потому что функция %trim() удаляет любое цитирование макроса перед возвратом результата. Примерно так:

%put %unquote(%trim(&param));

Этого можно избежать, повторно цитируя возвращаемый результат с помощью% str () еще раз:

%put %left(%str(%trim(&param)));

... или заключить исходный параметр в% nrstr ():

%let param = %str(     inobs = 2        );
%put %left(%trim(%nrstr(&param)));

... или с помощью %sysfunc() для вызова функции шага данных:

%put %sysfunc(compress(&param));
person Robert Penridge    schedule 24.11.2015
comment
Я думаю, что в целом согласен с тем, что вы здесь говорите, так что +1 ... но я бы хотел добавить к этому две вещи: во-первых, добавить вторую стадию, которую он должен использовать здесь %left ( второй %str около %trim) и два, вероятно, что-то о том, почему они тоже не нужны ... - person Joe; 25.11.2015
comment
@Joe Хорошо, готово. Это немного беспорядочно, но разговор о цитировании макросов похож на открытие ящика Пандоры. - person Robert Penridge; 25.11.2015
comment
Почему голос против? Как этот правильный ответ «бесполезен»? знак равно - person Robert Penridge; 25.11.2015

Если вы удалите условие %if следующим образом, оно должно сработать:

%macro pass_parameter_with_equal_sign(table=, sqlOptions=);
    proc sql 
    &sqlOptions.
    /* the semicolon to end the proc sql statement */
    ;
        create table macro_output as 
            select * 
            from have;
    quit;
%mend;

%if, который вы использовали, - это проверить, не является ли &sqlOptions пустым, это не имеет значения, если вы используете его как есть, потому что его безусловное использование даст либо:

proc sql inobs=2; /* in the case of &sqlOptions=inobs=2 */

или если для &sqlOptions не указано значение, вы должны увидеть:

proc sql; /* i.e. no options specified */

Так что он должен работать с аргументом или без него.

person Amir    schedule 24.11.2015

Решение Амира, вероятно, подходит для вашего конкретного случая использования. Но чтобы ответить на более общий вопрос, нам нужно обратиться к основополагающей статье о тестировании макропараметров, Чан Чанга. Этот параметр макроса пуст?.

Его пример C8 подходит вам здесь, хотя некоторые другие также подойдут.

%if %sysevalf(%superq(param)=,boolean) %then ... /* C8 */ 

Например:

%macro test_me(param=);

  %if %sysevalf(%superq(param)=,boolean) %then %put Empty;
  %else %put Not Empty;;
%mend test_me;

%test_me(param=);
%test_me(param=MyParam);
%test_me(param=param=5);

% SUPERQ здесь наиболее полезен, потому что он позволяет избежать разрешения макроса. Вместо этого он сохраняет его как значение макропараметра - полностью неразрешенное - и позволяет вам работать с ним таким образом; так что вы не рискуете, что этот надоедливый знак равенства вас побеспокоит.

Его C4 (просто с использованием SUPERQ без SYSEVALF) также работает в этом случае, хотя он объясняет несколько ситуаций, в которых могут возникнуть трудности.

person Joe    schedule 24.11.2015