Using SAS/IML Software to Generate IML Statements |
The ability to form and submit statements dynamically provides a very powerful mechanism for making systems flexible. For example, consider the building of a data entry system for a file. It is straightforward to write a system by using WINDOW and DISPLAY statements for the data entry and data processing statements for the I/O, but once you get the system built, it is good only for that one file. With the ability to push statements dynamically, however, it is possible to make a system that dynamically generates the components that are customized for each file. For example, you can change your systems from static systems to dynamic systems.
To illustrate this point, consider an IML system to edit an arbitrary file, a system like the FSEDIT procedure in SAS/FSP software but programmed in IML. You cannot just write it with open code because the I/O statements hardcode the filenames and the WINDOW and DISPLAY statements must hardcode the fields. However, if you generate just these components dynamically, the problem is solved for any file, not just one. Here is the code:
proc iml; /* FSEDIT */ /* This program defines and stores the modules FSEINIT, */ /* FSEDT, FSEDIT, and FSETERM in a storage catalog called */ /* FSED. To use it, load the modules and issue the command */ /* RUN FSEDIT; The system prompts or menus the files and */ /* variables to edit, then runs a full screen editing */ /* routine that behaves similar to PROC FSEDIT */ /* */ /* These commands are currently supported: */ /* */ /* END gets out of the system. The user is prompted */ /* as to whether or not to close the files and */ /* window. */ /* SUBMIT forces current values to be written out, */ /* either to append a new record or replace */ /* existing ones */ /* ADD displays a screen variable with blank values */ /* for appending to the end of a file */ /* DUP takes the current values and appends them to */ /* the end of the file */ /* number goes to that line number */ /* DELETE deletes the current record after confirmation */ /* by a Y response */ /* FORWARD1 moves to the next record, unless at eof */ /* BACKWARD1 moves to the previous record, unless at eof */ /* EXEC executes any IML statement */ /* FIND finds records and displays them */ /* */ /* Use: proc iml; */ /* reset storage='fsed'; */ /* load module=_all_; */ /* run fsedit; */ /* */ /*---routine to set up display values for new problem--- */ start fseinit; window fsed0 rows=15 columns=60 icolumn=18 color='GRAY' cmndline=cmnd group=title +30 'Editing a data set' color='BLUE'; /*---get file name--- */ _file=" "; msg = 'Please Enter Data Set Name or Nothing For Selection List'; display fsed0.title, fsed0 ( / @5 'Enter Data Set:' +1 _file +4 '(or nothing to get selection list)' ); if _file=' ' then do; loop: _f=datasets(); _nf=nrow(_f); _sel=repeat("_",_nf,1); display fsed0.title, fsed0 (/ "Select? File Name"/) , fsed0 (/ @5 _sel +1 _f protect=yes ) repeat ; _l = loc(_sel^='_'); if nrow(_l)^=1 then do; msg='Enter one S somewhere'; goto loop; end; _file = _f[_l]; end; /*---open file, get number of records--- */ call queue(" edit ",_file,"; setin ",_file," NOBS _nobs; resume;"); pause *; /*---get variables--- */ _var = contents(); _nv = nrow(_var); _sel = repeat("_",_nv,1); display fsed0.title, fsed0 (/ "File:" _file) noinput, fsed0 (/ @10 'Enter S to select each var, or select none to get all.' // @3 'select? Variable ' ), fsed0 ( / @5 _sel +5 _var protect=yes ) repeat; /*---reopen if subset of variables--- */ if any(_sel^='_') then do; _var = _var[loc(_sel^='_')]; _nv = nrow(_var); call push('close ',_file,'; edit ',_file,' var _var;resume;');pause *; end; /*---close old window--- */ window close=fsed0; /*---make the window---*/ call queue('window fsed columns=55 icolumn=25 cmndline=cmnd msgline=msg ', 'group=var/@20 "Record " _obs protect=yes'); call queue( concat('/"',_var,': " color="YELLOW" ', _var,' color="WHITE"')); call queue(';'); /*---make a missing routine---*/ call queue('start vmiss; '); do i=1 to _nv; val = value(_var[i]); if type(val)='N' then call queue(_var[i],'=.;'); else call queue(_var[i],'="', cshape(' ',1,1,nleng(val)),'";'); end; call queue('finish; resume;'); pause *; /*---initialize current observation---*/ _obs = 1; msg = Concat('Now Editing File ',_file); finish; /* */ /*---The Editor Runtime Controller--- */ start fsedt; _old = 0; go=1; do while(go); /*--get any needed data--*/ if any(_obs^=_old) then do; read point _obs; _old = _obs; end; /*---display the record---*/ display fsed.var repeat; cmnd = upcase(left(cmnd)); msg=' '; if cmnd='END' then go=0; else if cmnd='SUBMIT' then do; if _obs<=_nobs then do; replace point _obs; msg='replaced'; end; else do; append; _nobs=_nobs+nrow(_obs); msg='appended'; end; end; else if cmnd="ADD" then do; run vmiss; _obs = _nobs+1; msg='New Record'; end; else if cmnd='DUP' then do; append; _nobs=_nobs+1; _obs=_nobs; msg='As Duplicated'; end; else if cmnd>'0' & cmnd<'999999' then do; _obs = num(cmnd); msg=concat('record number ',cmnd); end; else if cmnd='FORWARD1' then _obs=min(_obs+1,_nobs); else if cmnd='BACKWARD1' then _obs=max(_obs-1,1); else if cmnd='DELETE' then do; records=cshape(char(_obs,5),1,1); msg=concat('Enter command Y to Confirm delete of' ,records); display fsed.var repeat; if (upcase(cmnd)='Y') then do; delete point _obs; _obs=1; msg=concat('Deleted Records',records); end; else msg='Not Confirmed, Not Deleted'; end; else if substr(cmnd,1,4)='FIND' then do; call execute("find all where(", substr(cmnd,5), ") into _obs;" ); _nfound=nrow(_obs); if _nfound=0 then do; _obs=1; msg='Not Found'; end; else do; msg=concat("Found ",char(_nfound,5)," records"); end; end; else if substr(cmnd,1,4)='EXEC' then do; msg=substr(cmnd,5); call execute(msg); end; else msg='Unrecognized Command; Use END to exit.'; end; finish; /*---routine to close files and windows, clean up---*/ start fseterm; window close=fsed; call execute('close ',_file,';'); free _q; finish; /*---main routine for FSEDIT---*/ start fsedit; if (nrow(_q)=0) then do; run fseinit; end; else msg = concat('Returning to Edit File ',_file); run fsedt; _q='_'; display fsed ( "Enter 'q' if you want to close files and windows" _q " (anything else if you want to return later" pause 'paused before termination'; run fseterm; finish; reset storage='fsed'; store module=_all_;
Copyright © 2009 by SAS Institute Inc., Cary, NC, USA. All rights reserved.