Markku Suni Turun ammattikorkeakoulu Dynaamista ja joustavaa ohjelmointia - maukasta makrokielellä
SAS Makrokieli SAS Makrokieli on kieli SAS-kielen laajennus datavaihetta muistuttavia lauseita ja funktioita makroprosessorin käynnistää ohjelmasta löytyvä %nimi tai &nimi
SAS Makrokieli Makrokielen asiat tapahtuvat ohjelmaa käännettäessä makroprosessorin työn tulos näkyy kääntäjälle Idea: yksi nimi korvautuu pitemmällä tekstillä moneen kertaan monessa kohtaa ohjelmaa mahdollisesti vaihtelevasti kyse on aina tekstistä, merkkijonosta Tämä tapahtuu ohjelmaa käännettäessä
SAS Makrokielen teoriaa SAS-ohjelma lähetetty suoritukseen: SUBMIT Scanner selaa ohjelmaa Tokenizer etsii kokonaisuutta (nimi tms.) Löytyy merkki % tai & Makroprosessori herätetään; lukee merkkiä seuraavan sanan Makroprosessori toimii sanan edellyttämällä tavalla - mahdollisesti korvaa sanan tekstillä - päivittää taulujaan - lukee sanan uudelleen, tekee em. operaatiot - hävittää lukemansa sanan Tokenizer jatkaa merkkiä seuraavasta kohdasta
Voidaan määritellä melkein milloin tahansa Sisältää saman arvon kunnes eksplisiittisesti muutetaan muuten sama koko ohjelman (istunnon) ajan Määrittely ja asetus: %let nimi = uusi ; Viittaus: Makromuuttujat &nimi Havaitaan: &nimi.. Ei: &nimi Kyseinen kohta ohjelmassa korvautuu makron arvolla Makron arvo on aina merkkijono!! SAS-kääntäjä voi ymmärtää sen lukuna
Makromuuttujat Makromuuttuja voi sisältää SAS-ohjelmaa: %let koodi = %str( proc freq; table sex * vastaus; ); %str-funktion sisällä puolipisteet eivät päätä arvoa Makromuuttujan tyhjennys: %let koodi = ; Sisäkkäistys: %let koodi = %str( proc freq; table &var*&var2; ); Muuttujan arvo lokiin: %put &var ;
Makromuuttujat Makromuuttuja voi olla yksittäinen sana tai osa sitä: %let nobsi = ; tai %let nobsi = noobs ; proc print data = &tdsto &nobsi; var &idmja maksu korvaus; Makromuuttuja tekstin osana: data &alku.yks ; set tieto&loppu &kirjasto..tiedot; Piste lopettaa makromuuttujan nimen jos sitä ei muuten huomaisi. Huom.: kaksi pistettä: toinen jää paikalleen.
Makro Makro on pala koodia, jolle annetaan nimi %macro dsn; data.tiedot %mend dsn; %macro suorita; proc print data = %dsn; %mend suorita;
Makro Makro on pala koodia, jolle annetaan nimi %macro dsn; data.tiedot %mend dsn; %macro suorita; proc print data = %dsn; %mend suorita; Nyt käyttö: title1 Tiedosto %dsn käytössä ; %suorita; Makro voi generoida hyvinkin pitkän pätkän koodia Puolipiste ei ole pakollinen makrokutsun jäljessä.
Makro ja parametrit Makroon voidaan lisätä parametreja: %macro suorita( tdsto, bymja ); proc print data = &tdsto; by &bymja; %mend suorita;
Makroon voidaan lisätä parametreja: %macro suorita( tdsto, bymja ); proc print data = &tdsto; by &bymja; %mend suorita; Kutsussa: Makro ja parametrit %suorita( data.testi, kunta ) %suorita( kunnat, kunta ); Parametreja EI laiteta sitaatteihin (AINA merkkijono). %analyysi( tiedot, 5, maksu, 1 );
Makro ja parametrit Parametrit kutsussa pilkuilla erotettuina %suorita( tdsto, bymja ); Puuttuva parametrin arvo korvautuu tyhjällä: %suorita( data.testi ); %suorita(, kunta ); Tällöin parametria vastaava kohta koodia jää tyhjäksi = ei mitään SAS-koodista riippuen voi olla virhe tai sitten ei ole PROC print data = tiedot &nobsi ;
Makro ja parametrit Parametrit korvataan siis position perusteella %macro sortti ( tdsto, by1, by2, by3 ); proc sort data = &tdsto ; by &by1 &by2 &by3; Toimii, jos on ainakin BY1 annettu. Muut voi jättää pois. Toinen mahdollinen yleistys: %macro sorttaa ( tdsto, bylista ); prod sort data = &tdsto ; by &bylista; Ja sitten vaan: %sortti ( tiedot, yhtio, alue, konttori ); %sortti ( tiedot, yhtio ); %sorttaa( tiedot, yhtio alue konttori ); %sorttaa( tiedot, yhtio );
Makro ja parametrit Makron parametreilla voi olla oletusarvoja: %macro suorita( tdsto=tiedot, bymja=alue);... ; %mend suorita; Ellei parametrille annettu arvoa, käytetään määrityksen oletusarvoa %suorita( tdsto=data.testi, bymja=konttori ); %suorita( tdsto=data.testi ); %suorita( );
Makrojen sisäkkäisyys %macro teetdsto; tapa 1 data temp; set data.tiedot; if age >= 20; %mend teetdsto; %macro listaa; proc print data = temp; by &bymja; %mend listaa; %macro analyysi ( bymja ); %* luo tiedosto - kommentti makrossa ; %teetdsto; %* sitten listataan ; %listaa ; %mend analyysi;
Makrojen sisäkkäisyys %macro analyysi ( bymja ); tapa 2 %macro teetdsto; data temp; set data.tiedot; if age >= 20; %mend teetdsto; %macro listaa; proc print data = temp; by &bymja; %mend listaa; %* luo tiedosto - kommentti makrossa ; %teetdsto; %* sitten listataan ; %listaa ; %mend analyysi; Makron suoritus määrittelee muita makroja
%macro analyysi ( bymja ); tapa 2 %macro teetdsto; data temp; set data.tiedot; if age >= 20; %mend teetdsto; %macro listaa; proc print data = temp; by &bymja; %mend listaa; %* luo tiedosto - kommentti makrossa ; %teetdsto; %* sitten listataan ; %listaa ; %mend analyysi; Makrojen sisäkkäisyys Ja itse ohjelma: %analyysi ( sukupuoli )
%macro printtaa( by ); proc print data = tiedot; %if &by = K %then %do; by yhtio; %end; id yhtio; var maksu korvaus; %mend printtaa; Ehdollinen koodi makrossa IF-then-rakenne toimii kuin SAS-kielessä, mutta makron tasolla (ennen käännöstä) Ero: ei sitaatteja merkkijonon ympärille %then-sanan jälkeen tulee makron kutsu (nimi: %suorita ) tai %do... %end
Ehdollinen koodi makrossa %macro tabuloi( yhtio ); proc tabulate data = tiedot; where yhtio = &yhtio ; class %if &yhtio ne 42 %then %do; alue piiri %end; myyja ; var maksu korvaus; table %if &yhtio ne 42 %then %do; alue * piiri * %end; myyja, ( maksu korvaus ) * sum; %mend tabuloi; Makrolauseiden ja lausekkeiden taittoon syytä paneutua (luettavuus)
Ehdollinen koodi makrossa Vertailussa ero SAS-kieleen nähden: %macro tabuloi( vipu, tdsto ); %if &vipu ne %then %do; proc tabulate data = &tdsto ;. Run; %end; %else %do; %* Ei siis tabuloida ; /* Ei siis tabuloida */ %end; %mend tabuloi;
Ehdollinen koodi makrossa Merkkijonojen kyseessä ollen muista ISOT vs pienet kirjaimet: %if %upcase( &firma ) = TurkuAMK %then %do; Makroprosessorille TurkuAMK ne turkuamk ne TURKUAMK Esim: OPTIONS pageno = 1; %tabuloi( TSK ); options pageno = 1; %tabuloi( Tai ); options pageno = 1; %tabuloi( Bio );
Toisto on mahdollista: %macro nimet( nimi, kpl ); Merkkijono ymmärretään lukuna %do n = 1 %to &kpl; &nimi&n Ei tarvita pistettä %end; %mend nimet; Proc print data = tiedot; var %nimet( mja, 4 ); => var mja1 mja2 mja3 mja4 ; Mutta: Toistokoodi makrossa %macro nimet( nimi, kpl ); Merkkijono ymmärretään lukuna %do n = 1 %to &kpl; &nimi.n&n Nyt on piste %end; %mend nimet; Proc print data = tiedot; var %nimet( mja, 4 ); => var mjan1 mjan2 mjan3 mjan4 ;
Datavaiheen liittymä Rutiinilla SYMPUT voidaan luoda ja asettaa makromuuttujia: CALL SYMPUT( yksikko_, 042 ); CALL SYMPUT( myksikko_, TSK ); CALL SYMPUT( myhtio_, yhtio ); CALL SYMPUT( mja, arvo ); Makromuuttujan nimi sitaatteihin, arvo samoin Jos arvo on merkkijonomuuttujassa, ei sitaatteja. Arvo saadaan datavaiheen muuttujaan: arvo = SYMGET( &myhtio_ ); Huom: SYMPUTin kutsun jälkeen on oltava RUN; että makromuuttuja olisi käytettävissä ( uusi käännös!! ) Näin luoduista makroista tulee globaaleja
Makromuuttuja voidaaan luoda SQL-kyselyssä. SQL-kyselystä puheen ollen... Arvojen lista voisi olla hyödyllinen: data pomot; input nimi $ ; cards; Nilken Murikka ; Proq sql noprint; select quote(nimi) from quit; data palkat; Makromuuttuja ja SQL into :nimet separated by pomot set henre.palkat ( where= ( nimi IN ( &nimet ))); INTO ohjaa tuloksen makromuuttujaan
Monimutkaisempaa - rescan Jos löytyy &:n jälkeen toinen &, makroprosessori korvaa && & ja lukee tekstin uudelleen Ja sama jatkuu &&&&x &&&x &&x &x Tätä hyväksikäyttäen voidaan rakentaa epäsuoria makroviittauksia
Monimutkaisempaa - rescan %let k010 = TSK ; %let k020 = BIO; %let k030 = TERV ; data a; do koulohj = '010', '020', '030'; do suunta = 1 to 3; do tapaus = 1 to 10; opisk = ranuni(1107)*10; opcred = ranuni(1107)* 80; output a; end; end; end; %macro printti( kohj ); proc tabulate data = a ; where koulohj = "&kohj"; class suunta; var opisk opcred; table luokka, (maksu korvaus)*sum; title Koulohjelma: &&k&kohj, opiskelijain suoritukset"; %mend printti; %printti( 010 ); %printti( 020 ); Mitä tässä tapahtuukaan? %printti( 030 );
Monimutkaisempaa - rescan %let k010 = TSK ; %let k020 = BIO; %let k030 = TERV ; data a; do koulohj = '010', '020', '030'; do suunta = 1 to 3; do tapaus = 1 to 10; opisk = ranuni(1107)*10; opcred = ranuni(1107)* 80; output a; end; end; end; %macro printti( kohj ); proc tabulate data = a ; where koulohj = "&kohj"; class suunta; var opisk opcred; table luokka, (maksu korvaus)*sum; title Koulohjelma: &&k&kohj, opiskelijain suoritukset"; %mend printti; %printti( 010 ); &k010 TSK
Esimerkki Halutun kuun alku ja loppu : %macro mkuual( pvm ) /* pvm: SAS-päivämäärämuuttuja */ alkukuu = MDY( MONTH( pvm ), 1, YEAR( pvm )); %mend mkuual; %macro mkuulop( pvm ) /* pvm: SAS-päivämäärämuuttuja */ if MONTH( pvm ) = 12 then lopukuu = MDY( 1, 1, YEAR( pvm ) + 1 ) -1; else lopukuu = MDY( MONTH( pvm ) + 1, 1, YEAR( pvm )) -1; %mend mkuulop;
Esimerkki Onko kyse testistä vai tuotantoajosta? Kirjastojen nimiä, viitteitä, tms. on hyvä laittaa makromuuttujiin. Ohjelman alussa selvitetään (vaikkapa ajoparametritiedosto lukemalla), onko kyseessä testi vai ei. Asetetaan makromuuttujat sopivasti. %macro alustus; DATA null; INFILE ajoparm; INPUT parametrit ; %* rakennellaan tarvittavat arvot ja sitten muuttujiin; CALL SYMPUT( ymp, TUOT ); CALL SYMPUT(.... ); RUN; %mend alustus; Ja sitten:... SET &ymp..tiedot;...
Esimerkki Raportointiohjelma: samantapainen raportti jokaisesta koulutusohjelmasta ja oppiaineesta %macro poimi( kohj, vv ) %* poimitaan ohjelma ja vuosi; data otos; set tieto; WHERE ( kohj = &kohj ) AND (vuosi = &vv); /* tehdään sitä ja tätä */ %mend poimi; %macro raportoi( kohj, vv ) %* raportti; %poimi( &kohj, vv ); %* poiminta ; PROC TABULATE data = otos ; CLASS %if &kohj = 10 %then %do oaine %end; sv ; VAR... RUN; %mend raportoi;
Raportointiohjelma: samantapainen raportti jokaisesta koulutusohjelmasta ja oppiaineesta Ja sitten se itse ohjelma: %raportoi( 10, 2011 ); %raportoi( 10, 2012 ); %raportoi( 20, 2011 ); %raportoi( 20, 2012 ); %raportoi( 30, 2011 ); %raportoi( 30, 2012 ); RUN; Esimerkki
Esimerkki Proseduurissa TABULATE on paha puute: luokat, joita vastaavia havaintoja ei ole, eivät näy tulosraportissa, eikä voida ilmoittaa, mitä arvoja siellä voisi olla (vrt proseduurien CHART ja GCHART optio MIDPOINTS). Q&D-ratkaisu: liitetään alkuperäisen havaintoaineiston jatkoksi tyhjä aineisto, jossa on halutut luokkamuuttujien arvot, mutta ei muuta.
Esimerkki %macro tabulk( x ) / parmbuff ; %let stop = %length( &syspbuff ); %let seti = %scan( &syspbuff, 1 ); %let nimi = %scan( &syspbuff, 2 ); data _mz_ ( keep = &nimi ); %do I = 3 %to &stop; %let arvo = %scan( &syspbuff, &i ); %if &arvo ne %then %do; &nimi = &arvo; output; %end; %else %let i = &stop; %end; proc append base = &seti data = _mz_; %mend tabulk;
Esimerkki Makron käyttö data koe; input luokka markka myynti; cards; 1 100 200 1 100 200 3 300 50 3 200 60 5 100 50 5 200 60 ; %tabulk( koe, luokka, 1, 2, 3, 4, 5 ); proc tabulate data = koe; class luokka; var markka myynti; table luokka all, ( markka myynti )* sum/ misstext = Puuttuu ;
Esimerkki Ja tulokseksi tulee:
Makroihin liittyviä asioita MLOGIC - NOMLOGIC Jos päällä, makroprosessori listaa lokiin makromuuttujien arvojen mukaan tapahtuneet haarautumisensa MPRINT - NOMPRINT Jos päällä, makroprosessori listaa lokiin makroista generoituneet SAS-lauseet (huom: ei aina kovin selvästi) SAS tarjoaa käyttöön koko joukon automaattisesti asetettuja makromuuttujia, esim: SYSDATE systeemin tuntema päivämäärä (tänään: 11OCT12 ) SYSLAST - viimeksi luodun SAS-tiedoston nimi muodossa viite.nimi SYSTIME - tämän SAS-istunnon tai työn aloitusajankohdan (hh:mm).
Yhteenveto Pieni sana, %macro, antaa valtavasti vaihtelua ohjelmaan. Makrojen ja makromuuttujien avulla voi ohjelmista tehdä joustavampia helpompia ylläpitää selkeitä: muuttuvat asiat ohjelman alussa hyvin nähtävillä lyhyempiä paremmin uudelleenkäytettäviä lisäämättä kohtuuttomasti ohjelman tekemisen vaikeutta
This is the end The Doors