我想从我创建的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函数来找出任何特定数据集中有多少行.
编写函数式宏的问题是什么?
即你可以用作的宏%let myVar = %myMacro(myArgument)
%doSomething(withSometing)像宏函数%let someVar =语句为宏变量赋值&myResult.在你的前一行写完%mendproc或一个data步骤,这就不再起作用了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)
我知道这很复杂,但至少所有讨厌的工作都已完成.您可以在老板接受的时间内复制,粘贴和修改此内容.;
除非您仅使用宏语句编写函数式宏,否则无法从函数式宏“返回”值。Quentin 的链接提供了如何执行此操作的示例。
例如,您不能像这样使用宏,因为 proc sql 无法在语句中间执行%put(这可以使用其他更复杂的解决方法,例如dosubl,但不是您编写的方式)。
%put %nobs(mylib,mydata);
Run Code Online (Sandbox Code Playgroud)
在不进行重大更改的情况下,您可以做的最好的事情是创建一个全局宏变量并在后续语句中使用它。
要创建原始宏的本地宏变量,您必须首先通过%local宏定义中的语句声明它。