SAS - 如何从SAS宏返回值?

bam*_*mbi 12 sas

我想从我创建的SAS宏中返回一个值,但我不确定如何.宏计算数据集中的观察数.我希望返回观察数量.

%macro nobs(library_name, table_name);
  proc sql noprint;
    select nlobs into :nobs
    from dictionary.tables
    where libname = UPCASE(&library_name)
      and memname = UPCASE(&table_name);
  quit;

  *return nobs macro variable;
  &nobs
%mend;

%let num_of_observations = %nobs('work', 'patients');
Run Code Online (Sandbox Code Playgroud)

另外,我希望&nobs宏中使用的宏变量对于该宏是本地的而不是全局的.我怎样才能做到这一点?

Joe*_*Joe 12

我将回答Bambi在评论中提出的核心问题:

我主要关注的是如何从宏返回一个值.

我将以一种重要的方式与Dirk在这里狡辩.他说:

SAS宏插入代码.它永远不会返回值,但在某些情况下,您可以模仿函数

我不同意.SAS宏返回插入到处理流中的文本.返回绝对是一个合适的术语.当文本恰好是单个数字时,可以说它返回一个值.

但是,如果宏除了该值之外只有宏语句,则它只能返回单个.意思是,每一行都必须以a开头%.任何不能开始的东西%都将被返回(并且一些开头的东西也%可能被返回).

所以重要的问题是,如何从宏中返回一个值.


在某些情况下,就像这个一样,完全有可能只有宏代码.事实上,在许多情况下,这在技术上是可行的 - 尽管在许多情况下,它比你应该做的更多.

杰克汉密尔顿的链接论文包含了一个适合的例子.他驳斥这个例子,但是这主要是因为他的论文是如何计算的观察中的情况下NOBS是错误的 -无论是用WHERE子句,或在数据集已被修改,而不NOBS的元数据被更新某些其他情况.

在你的情况下,你似乎非常乐意相信NOBS - 所以这个例子就可以了.

返回值的宏必须只有一个语句,它不是宏语法语句,或者一个宏语法语句,它将值返回到处理流中. %sysfunc这是一个声明的例子.喜欢的东西%let,%put,%if等是不(本身)返回任何语法报表; 所以你可以拥有你想要的那么多.

您还必须有一个语句在处理流中放置一个值:否则您根本不会从宏中获取任何内容.

这是第3页末尾的杰克宏的精简版,简化为删除nlobsf他显示的错误:

 %macro check;

   %let dsid = %sysfunc(open(sashelp.class, IS));
   %if &DSID = 0 %then
   %put %sysfunc(sysmsg());

   %let nlobs = %sysfunc(attrn(&dsid, NLOBS));

   %put &nlobs;

   %let rc = %sysfunc(close(&dsid));

 %mend;
Run Code Online (Sandbox Code Playgroud)

该宏不是函数式宏.它不会向处理流返回任何内容!它对于查看日志非常有用,但对于为您提供可编程的值非常有用.但是,它是功能样式宏的良好开端,因为你真正想要的是&nlobs,对吧?

 %macro check;

   %let dsid = %sysfunc(open(sashelp.class, IS));
   %if &DSID = 0 %then
   %put %sysfunc(sysmsg());

   %let nlobs = %sysfunc(attrn(&dsid, NLOBS));

   &nlobs

   %let rc = %sysfunc(close(&dsid));

 %mend;
Run Code Online (Sandbox Code Playgroud)

现在这是一个函数式宏:它有一个语句,它不是一个宏语法语句,&nlobs.在一个普通的行上都是它自己.

它实际上超过了你需要的一个陈述; 还记得我说过如何%sysfunc向处理流返回一个值吗?你可以删除该%let声明的一部分,留下你

 %sysfunc(attrn(&dsid, NLOBS))
Run Code Online (Sandbox Code Playgroud)

然后将值直接放在处理流本身 - 允许您直接使用它.当然,如果出现问题,调试并不容易,但我相信如果你需要,你可以解决这个问题.还要注意语句末尾没有分号 - 这是因为执行宏函数不需要分号,我们不想返回任何无关的分号.

让我们表现良好并添加一些%locals来获得这个美观和安全,并使数据集的名称成为参数,因为大自然厌恶没有参数的宏:

 %macro check(dsetname=);

   %local dsid nlobs rc;

   %let dsid = %sysfunc(open(&dsetname., IS));
   %if &DSID = 0 %then
   %put %sysfunc(sysmsg());

   %let nlobs = %sysfunc(attrn(&dsid, NLOBS));

   &nlobs

   %let rc = %sysfunc(close(&dsid));

 %mend;

 %let classobs= %check(dsetname=sashelp.class);

 %put &=classobs;
Run Code Online (Sandbox Code Playgroud)

你有它:一个函数式宏,它使用该nlobs函数来找出任何特定数据集中有多少行.

  • 天哪,@ Joe,我来写一个答案,发现你已经写过了,所以我得等下一次.也就是说,我会删除你的宏返回的分号:`&nlobs;`.通常,宏函数只返回值,不带分号,因此可以在语句中间调用它.我是Peter Crawford的简单宏实用程序%NOW的忠实粉丝,以及他描述它的宏功能设计论文:http://www2.sas.com/proceedings/sugi31/038-31.pdf.理解宏函数改变了我对宏语言的理解. (3认同)

Dir*_*ten 9

编写函数式宏的问题是什么?

即你可以用作的宏%let myVar = %myMacro(myArgument)

  • 您可以使用用户编写的宏,就好像它是一个函数一样,如果您只是这样做
    • 调用一些%doSomething(withSometing)像宏函数
    • 使用%let someVar =语句为宏变量赋值
    • "返回"你的结果,通常是&myResult.在你的前一行写完%mend
  • 只要在宏中包含一个proc或一个data步骤,这就不再起作用了
  • 幸运的是,%sysFunc()可以解决问题,因此我们可以使用任何数据步骤功能
  • 这包括像低级别的功能open,fetch并且close它甚至可以访问您的数据
  • 书呆子的人可以做很多事,但即使你是书呆子,老板也很少给你时间.

我们如何解决这个问题?,即我用哪个构建块来解决这个问题?

  • proc fcmp 允许在子例程或函数中打包一些数据步骤语句
  • 此功能可用于数据步骤,可在其中使用 %sysfunc()
  • 在此功能中,您可以调用run_macro立即执行任何宏背景

现在我们已准备好实际解决方案

第1步:编写一个辅助宏

  • 没有参数,
  • 使用一些全局宏变量
  • 将其结果"返回"全局宏变量

我知道这是一个糟糕的编码习惯,但为了降低风险,我们使用前缀限定这些变量.适用于问题中的示例

** macro nobsHelper retrieves the number of observations in a dataset
    Uses global macro variables:
        nobsHelper_lib: the library in which the dataset resides, enclosed in quotes
        nobsHelper_mem: the name of the dataset, enclosed in quotes
    Writes global macro variable:
        nobsHelper_obs: the number of observations in the dataset 

    Take care nobsHelper exists before calling this macro, or it will be ost
**;
%macro nobsHelper();
    ** Make sure nobsHelper_obs is a global macro variable**;
    %global nobsHelper_obs;

    proc sql noprint;
        select nobs
        into :nobsHelper_obs
        from sashelp.vtable
        where libname = %UPCASE(&nobsHelper_lib)
          and memname = %UPCASE(&nobsHelper_mem);
    quit;
    %* uncomment these put statements to debug **;
    %*put NOTE: inside nobsHelper, the following macro variables are known;
    %*put _user_;
%mend;
Run Code Online (Sandbox Code Playgroud)

第2步:编写辅助函数 ;

**Functions need to be stored in a compilation library;
options cmplib=sasuser.funcs;

** function nobsHelper, retrieves the number of observations in a dataset
    Writes global macro variables:
        nobsHelper_lib: the library in which the dataset resides, enclosed in quotes
        nobsHelper_mem: the name of the dataset, enclosed in quotes
    Calls the macro nobsHelper

    Uses macro variable:
        nobsHelper_obs: the number of observations in the dataset 
**;
proc fcmp outlib=sasuser.funcs.trial;
    ** Define the function and specity it should be called with two character vriables **;
    function nobsHelper(nobsHelper_lib $, nobsHelper_mem $);
        ** Call the macro and pass the variables as global macro variables 
        ** The macro variables will be magically qouted **;
        rc = run_macro('nobsHelper', nobsHelper_lib, nobsHelper_mem);
        if rc then put 'ERROR: calling nobsHelper gave ' rc=;

        ** Retreive the result and pass it on **;
        return (symget('nobsHelper_obs'));
    endsub;
quit;
Run Code Online (Sandbox Code Playgroud)

第3步:编写一个方便的宏来使用帮助器 ;

** macro nobs retrieves the number of observations in a dataset
    Parameters:
        library_name: the library in which the dataset resides
        member_name: the name of the dataset
    Inserts in your code:
        the number of observations in the dataset 
    Use as a function
**;
%macro nobs(library_name, member_name);
    %sysfunc(nobsHelper(&library_name, &member_name));

    %* Uncomment this to debug **;
    %*put _user_;
%mend;
Run Code Online (Sandbox Code Playgroud)

最后用它 ;

%let num_carrs = %nobs(sasHelp, cars);
%put There are &num_carrs cars in sasHelp.Cars;

Data aboutClass;
    libname = 'SASHELP';
    memname = 'CLASS';
    numerOfStudents = %nobs(sasHelp, class);
run;
Run Code Online (Sandbox Code Playgroud)

我知道这很复杂,但至少所有讨厌的工作都已完成.您可以在老板接受的时间内复制,粘贴和修改此内容.;


use*_*489 2

除非您仅使用宏语句编写函数式宏,否则无法从函数式宏“返回”值。Quentin 的链接提供了如何执行此操作的示例。

例如,您不能像这样使用宏,因为 proc sql 无法在语句中间执行%put(这可以使用其他更复杂的解决方法,例如dosubl,但不是您编写的方式)。

%put %nobs(mylib,mydata);
Run Code Online (Sandbox Code Playgroud)

在不进行重大更改的情况下,您可以做的最好的事情是创建一个全局宏变量并在后续语句中使用它。

要创建原始宏的本地宏变量,您必须首先通过%local宏定义中的语句声明它。