220 likes | 316 Views
recursie met de datastep. jeroen j van beele 1(1) april 2005. kun je een programma schrijven dat zichzelf als output heeft? of: in welke taal kun je dat doen? of: waar moet een taal aan voldoen om zo'n programma in te kunnen schrijven?.
E N D
recursie met de datastep jeroen j van beele 1(1) april 2005
kun je een programma schrijven dat zichzelf als output heeft? • of: in welke taal kun je dat doen? • of: waar moet een taal aan voldoen om zo'n programma in te kunnen schrijven?
dat kun je leuk combineren met recursie: een programma dat zichzelf schrijft en vervolgens ook inleest en uitvoert • dan moeten we de omgeving van die programmeertaal een beetje schetsen: • denk aan de sas-program editor van waaruit je schrijft naar en leest van externe bestanden
het kan bijvoorbeeld als de taal beschikt over de volgende twee opdrachten: • lees(<bestand>) • schrijf*(argument,<bestand>) waar schrijf* is: • herhaalt zichzelf • schrijft het argument naar <bestand> • het programma zou er dan alsvolgt uitzien: • schrijf*(lees(pgm),pgm)) • lees(pgm)
kan dit in de datastep? • dus: kun je een datastep schrijven die zichzelf schrijft? en dan natuurlijk %includen
filename rc000000 catalog "work.recurse.rc00000.source"; • data _null_; • file rc000000; • /* wat moet hier komen te staan? */ • run; • %include rc000000;
weten jullie nog andere richtingen? • het is een beetje ook de keuze die ik maak in de vraagstelling: • programma schrijft zichzelf, helemaal in de datastep, dus geen scl of macro's
filename rc000000 catalog "work.recurse.rc00000.source"; • data _null_; • file rc000000; • put "filename rc000000 catalog ""work.recurse.rc00000.source"";"; • put "data _null_;"; • put "file recurs;"; • put "run;"; • put "%include rc000000;"; • run; • %include rc000000;
dit houdt op, 2 ideeen • put(quote) • do-loop • (3e toevalstreffer) schrijf in code eerst de stap die het einde wegschrijft en daarna het begin • ik vind het moeilijk om uit te leggen waarom dit werkt:
het conceptuele schema is alsvolgt: • begincode; • put "begincode;"; • deze code heeft als resultaat: • begincode; • maar • put "begincode;"; • mist in de uitvoer • en put "begincode;"; kun je niet aan het argument begincode toevoegen want dan zit je conceptueel in een loop
wat wel kan is put aan begincode toevoegen • begincode; • put "begincode; put"; • dan is het resultaat: • begincode; • put • merk op dat dit resultaat het argument is van de put
als we nu alleen maar het argument herhalen hebben we de hele code gereproduceerd: • begincode; • regel="begincode; regel="; • put regel; • put quote(regel); • resultaat: • 1e put schrijft regel weg, resultaat: • begincode; • regel= • 2e put voert quote(regel) uit, resultaat: • "begincode; regel=" • samen is het resultaat • begincode; • regel="begincode; regel=" • merk op dat de ; aan het einde van regel 2 mist!
dan hebben we die twee putstatements natuurlijk nog, maar die zijn aanzienlijk minder ingewikkeld dan die hele begincode • en toen kwam de grote truc (het omdraaien van de schrijfvolgorde in de code) die ik bij toeval ontdekte en niet kan uitleggen (een beetje kan ik hem wel uitleggen: de clou is dat je door het verwisselen niet in je eigen staart bijt):
filename rc000000 catalog "work.recurse.rc00000.source"; • data _null_; • file rc000000; • do level=0 to 1; • if level then do; • regel="<eind>"; • put regel; • end; • else do; • regel="<begin>"; • put regel; • regel=quote(trim(regel)); • put regel; • end; • end; • run; • %include rc000000;
begin loopt van het begin tot aan de beginquote van "<begin>" • eind loopt van na de eindquote van "<begin>" tot het eind • LET OP: EERST moet je <eind> substitueren en DAARNA <begin> • de truc zit hem in de laatste (geschreven) put (die als middelste uitgevoerd wordt): • als de datastep runt schrijft de middelste (geschreven) put eerst <begin> naar rc000000 • daarna schrijft de laatste put <begin> nog eens op maar dan als argument van de regel= zodat de code t/m • regel="<begin>"; • klaar is, op de ; na! • die ; stoppen we dan aan het begin van <eind> • en dan zie je ook waarom de omwisseltruc werkt, want met die truc kunnen we <eind> in zijn geheel in <begin> opnemen (als je EERST <eind> substitueert en DAARNA <begin>)
dan kom je in een oneindige loop, • hoe kom je daar weer uit? • de antwoorden vind je hieronder:
filename rc000000 catalog "work.recurse.rc00000.source" lrecl=1000; • data _null_; • length regel $1000; • include_level=1; • put include_level=; • if include_level=30 then put "stop"; • else do put_level=0 to 1; • if put_level then do; • regel="<eind>"; • put regel; • end; • else do; • file rc000000; • regel="<begin>"; • put regel; • regel="<midden>"; • put regel; • regel=quote(trim(regel)); • put regel; • end; • end; • run; • %include rc000000;
hoe diep kan sas includes nesten? • om deze vraag te beantwoorden moet je de 30 aanpassen in de code (of documentatie raadplegen natuurlijk) • de eerste twee 30's doen niet terzake, die kun je weghalen (zie hieronder), de laatste is degene waar het om gaat • die heb ik vervangen door een macrovariabele, dus:
options noquotelenmax nosource nosource2 nonotes; • %let diepte=30; • filename rc000000 catalog "work.recurse.rc00000.source" lrecl=1000; • data _null_; • length regel $1000; • include_level=1; • put include_level=; • do put_level=0 to 1; • if put_level then do; • regel="; • put regel; • regel=quote(trim(regel)); • put regel; • end; • end; • run; • %include rc"||put(include_level,z6.)||";"; • put regel; • end; • else do; • file rc000000; • regel=" • filename rc"||put(include_level,z6.)||" catalog ""work.recurse.rc"||put(include_level,z6.)||".source"" lrecl=1000;
data _null_; • length regel $1000; • include_level="||put(include_level+1,6.)||"; • put include_level=; • do put_level=0 to 1; • if put_level then do; • regel=""; • put regel; • regel=quote(trim(regel)); • put regel; • end; • end; • run; • %include rc""||put(include_level,z6.)||"";""; • put regel; • end; • else do; • file rc"||put(include_level,z6.)||";"; • put regel; • regel=" • regel="" • filename rc""||put(include_level,z6.)||"" catalog """"work.recurse.rc""||put(include_level,z6.)||"".source"""" lrecl=1000;
data _null_; • length regel $1000; • include_level=""||put(include_level+1,6.)||""; • put include_level=; • if include_level=&diepte then put """"stop""""; • else do put_level=0 to 1; • if put_level then do; • regel=""""; • put regel; • regel=quote(trim(regel)); • put regel; • end; • end; • run; • %include rc""""||put(include_level,z6.)||"""";""""; • put regel; • end; • else do; • file rc""||put(include_level,z6.)||"";""; • put regel; • regel="; • put regel; • regel=quote(trim(regel)); • put regel; • end; • end; • run; • %include rc000000;
options noquotelenmax nosource nosource2 nonotes; • %let diepte=30; • filename rc000000 catalog "work.recurse.rc00000.source" lrecl=1000; • data _null_; • length regel $1000; • include_level=1; • put include_level=; • do put_level=0 to 1; • if put_level then do; • regel="; • put regel; • regel=quote(trim(regel)); • put regel; • end; • end; • run; • %include rc"||put(include_level,z6.)||";"; • put regel; • end; • else do; • file rc000000; • regel=" • filename rc"||put(include_level,z6.)||" catalog ""work.recurse.rc"||put(include_level,z6.)||".source"" lrecl=1000; • data _null_; • length regel $1000; • include_level="||put(include_level+1,6.)||"; • put include_level=; • do put_level=0 to 1; • if put_level then do; • regel=""; • put regel; • regel=quote(trim(regel)); • put regel; • end; • end; • run; • %include rc""||put(include_level,z6.)||"";""; • put regel; • end; • else do; • file rc"||put(include_level,z6.)||";"; • put regel; • regel=" • regel="" • filename rc""||put(include_level,z6.)||"" catalog """"work.recurse.rc""||put(include_level,z6.)||"".source"""" lrecl=1000; • data _null_; • length regel $1000; • include_level=""||put(include_level+1,6.)||""; • put include_level=; • if include_level=&diepte then put """"stop""""; • else do put_level=0 to 1; • if put_level then do; • regel=""""; • put regel; • regel=quote(trim(regel)); • put regel; • end; • end; • run; • %include rc""""||put(include_level,z6.)||"""";""""; • put regel; • end; • else do; • file rc""||put(include_level,z6.)||"";""; • put regel; • regel="; • put regel; • regel=quote(trim(regel)); • put regel; • end; • end; • run; • %include rc000000; • options noquotelenmax nosource nosource2 nonotes; • %let diepte=30; • filename rc000000 catalog "work.recurse.rc00000.source" lrecl=1000; • data _null_; • length regel $1000; • include_level=1; • put include_level=; • do put_level=0 to 1; • if put_level then do; • regel="; • put regel; • regel=quote(trim(regel)); • put regel; • end; • end; • run; • %include rc"||put(include_level,z6.)||";"; • put regel; • end; • else do; • file rc000000; • regel=" • filename rc"||put(include_level,z6.)||" catalog ""work.recurse.rc"||put(include_level,z6.)||".source"" lrecl=1000; • data _null_; • length regel $1000; • include_level="||put(include_level+1,6.)||"; • put include_level=; • do put_level=0 to 1; • if put_level then do; • regel=""; • put regel; • regel=quote(trim(regel)); • put regel; • end; • end; • run; • %include rc""||put(include_level,z6.)||"";""; • put regel; • end; • else do; • file rc"||put(include_level,z6.)||";"; • put regel; • regel=" • regel="" • filename rc""||put(include_level,z6.)||"" catalog """"work.recurse.rc""||put(include_level,z6.)||"".source"""" lrecl=1000; • data _null_; • length regel $1000; • include_level=""||put(include_level+1,6.)||""; • put include_level=; • if include_level=&diepte then put """"stop""""; • else do put_level=0 to 1; • if put_level then do; • regel=""""; • put regel; • regel=quote(trim(regel)); • put regel; • end; • end; • run; • %include rc""""||put(include_level,z6.)||"""";""""; • put regel; • end; • else do; • file rc""||put(include_level,z6.)||"";""; • put regel; • regel="; • put regel; • regel=quote(trim(regel)); • put regel; • end; • end; • run; • %include rc000000;