Size-change grafer
Size-change grafer
Size-change grafer
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
Bachelorprojekt<br />
<strong>Size</strong>-<strong>change</strong> terminationsanalyse af et imperativt<br />
programmeringssprog<br />
af Jakob Uhd Jepsen & Claus Rørbech & Espen Suenson<br />
Datalogisk Institut, Københavns Universitet<br />
Forår 2001
Indhold<br />
1 Sammenfatning 3<br />
2 Indledning 3<br />
3 <strong>Size</strong>-<strong>change</strong> terminationsprincippet for funktionelle programmeringssprog<br />
4<br />
3.1 Estimation af size-<strong>change</strong> <strong>grafer</strong> . . . . . . . . . . . . . . . . . . . 4<br />
3.2 Uendelige kaldsekvenser . . . . . . . . . . . . . . . . . . . . . . . 5<br />
4 Et imperativt programmeringssprog 6<br />
4.1 Syntaks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6<br />
4.2 Egenskaber . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6<br />
4.3 Forenklende begrænsninger . . . . . . . . . . . . . . . . . . . . . 7<br />
4.4 Semantik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7<br />
5 <strong>Size</strong>-<strong>change</strong> terminationsanalyse af et imperativt programmeringssprog<br />
8<br />
5.1 Programpunkter . . . . . . . . . . . . . . . . . . . . . . . . . . . 9<br />
5.2 <strong>Size</strong>-<strong>change</strong> <strong>grafer</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . 9<br />
5.2.1 Sikker estimering af værdier . . . . . . . . . . . . . . . . . 9<br />
5.3 Tråde . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10<br />
5.4 Returværdier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11<br />
5.5 Kaldsekvenser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11<br />
5.5.1 Flowanalyse . . . . . . . . . . . . . . . . . . . . . . . . . . 11<br />
5.6 Uendelige kaldsekvenser - simple og kombinerede cykler . . . . . 12<br />
5.7 Realiserbare kaldsekvenser . . . . . . . . . . . . . . . . . . . . . . 13<br />
5.8 Eksempel på size-<strong>change</strong> analyse . . . . . . . . . . . . . . . . . . 15<br />
6 Implementation af size-<strong>change</strong> terminationsanalyse 16<br />
7 Eksperimentelle resultater 16<br />
7.1 Testprogrammer . . . . . . . . . . . . . . . . . . . . . . . . . . . 16<br />
7.1.1 Jones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17<br />
7.1.2 Wahlstedt . . . . . . . . . . . . . . . . . . . . . . . . . . . 17<br />
7.1.3 Glenstrup . . . . . . . . . . . . . . . . . . . . . . . . . . . 18<br />
7.2 Opsummering af resultater . . . . . . . . . . . . . . . . . . . . . . 19<br />
8 Konklusion 20<br />
A Testprogrammer 23<br />
B Syntaks og semantik for et funktionelt programmeringssprog 29<br />
2
1 Sammenfatning<br />
På baggrund af size-<strong>change</strong> terminationsanalyse af funktionelle programmeringssprog<br />
præsenteret i [POPL01] konstrueres en metode til size-<strong>change</strong> terminationsanalyse<br />
af et imperativt sprog. Det viser sig, at analysen er en anelse<br />
mere kompliceret i det imperative tilfælde. En implementation af metoden evalueres,<br />
og eksperimenter viser, at metoden er brugbar om end ineffektiv til analyse<br />
af større programmer.<br />
2 Indledning<br />
Bestemmelsen af hvorvidt et program terminerer, er en vigtig del af at skrive<br />
meningsfyldte og korrekte programmer. Derfor er det eftertragtet at finde en<br />
metode der på baggrund af programmets struktur kan sige noget om termination.<br />
Det er imidlertid ikke muligt generelt automatisk at afgøre, om et givet program<br />
terminerer for alle inddata. Terminationsanalyse går ud på at estimere terminationsegenskaben,<br />
så man ved hjælp af en algoritme i visse tilfælde kan give et<br />
positivt svar på terminationsspørgsmålet.<br />
Denne rapport bygger på den gennemgang af size-<strong>change</strong> terminationsprincippet<br />
anvendt på funktionelle programmeringssprog 1 , der præsenteres i artiklen<br />
[POPL01]. Denne artikel forudsættes kendt af læseren. Motivationen for at beskæftige<br />
sig med terminationsanalyse er, jvf. denne artikel, at terminationsanalyse<br />
har anvendelse inden for blandt andet:<br />
Programverifikation<br />
Automatisk programtransformering<br />
Partiel evaluering<br />
Hertil kan tilføjes, at terminationsanalyse i sig selv er et videnskabeligt interessant<br />
emne.<br />
Formålet med denne rapport er at undersøge size-<strong>change</strong> terminationsprincippets<br />
anvendelsesmuligheder for et imperativt programmeringssprog. Vi søger<br />
at bestemme, i hvor høj grad fremgangsmåden i det funktionelle tilfælde kan<br />
overføres, og hvor der i givet fald opstår problemer ved overgangen til imperative<br />
programmeringssprog. Dette vil vi gøre ved at betragte et simpelt konkret<br />
eksempel.<br />
Derudover søger vi at afgøre, hvor anvendelig vores udviklede metode er i en<br />
faktisk implementation. Specielt vil vi diskutere metodens egnethed i forbindelse<br />
med større programmer.<br />
Den resterende del af rapporten er struktureret således:<br />
Afsnit 3 beskriver size-<strong>change</strong> termination for funktionelle sprog med henblik<br />
på at give en basis for sammenligning med den imperative version,<br />
som det er sigtet at udvikle. Afsnit 3 er en sammenfatning af de for os<br />
væsentlige dele af artiklen [POPL01].<br />
I afsnit 4 beskrives syntaks og semantik for det imperative sprog som tjener<br />
til at illustrere size-<strong>change</strong> termination for imperative sprog. Dette er<br />
kommenteret med overvejelser om centrale egenskaber ved sproget.<br />
1 Vi vil overalt i denne rapport indskrænke os til at tale om første ordens programmeringssprog.<br />
3
Afsnit 5 beskriver den udviklede analysemetode for sproget.<br />
Afsnit 6 beskriver en implementation af metoden.<br />
Afsnit 7 præsenterer eksperimentelle resultater ved anvendelse af analysemetoden<br />
på testprogrammer.<br />
3 <strong>Size</strong>-<strong>change</strong> terminationsprincippet for funktionelle<br />
programmeringssprog<br />
Den grundlæggende tankegang i size-<strong>change</strong> termination består i at betragte de<br />
relative ændringer i værdier af et programs parametre under udførelsen. Hvis<br />
det er muligt at opdage en tendens i denne udvikling, kan dette benyttes til at<br />
komme med positive udsagn om hvorvidt programmet terminerer.<br />
<strong>Size</strong>-<strong>change</strong> terminationspricippet antager, at der er en velfunderet ordning på<br />
de værdier parametrene i et programmeringssprog kan antage. For hvert funktionskald,<br />
der optræder i et givent program, konstrueres en size-<strong>change</strong> graf<br />
ud fra et konservativt estimat af sammenhængen mellem parametre i programmet<br />
mht. denne ordning. <strong>Size</strong>-<strong>change</strong> terminationskriteriet kan da formuleres<br />
således:<br />
Hvis der for enhver uendelig kaldsekvens, der kan opstå under udførelsen<br />
af et program, i sammensætningen af de size-<strong>change</strong> <strong>grafer</strong><br />
der beskriver denne kaldsekvens (følge af funktionskald), findes en<br />
uendelig nedadstigning i værdien af en parameter, er uendelig beregning<br />
ikke mulig i dette program dvs. programmet terminerer for<br />
alle inddata.<br />
Første trin i size-<strong>change</strong> terminationsanalysen er at estimere size-<strong>change</strong> <strong>grafer</strong><br />
for programmet. Andet trin er at anvende ovenstående princip på de mulige<br />
uendelige kaldsekvenser i programmet.<br />
Dette kapitel sammenfatter den fremgangsmåde der er beskrevet i [POPL01]. I<br />
denne artikel betragtes et simpelt første ordens funktionsprogrammeringssprog<br />
med call-by-value evalueringsstrategi. Sprogets syntaks og semantik kan ses i<br />
bilag B.<br />
3.1 Estimation af size-<strong>change</strong> <strong>grafer</strong><br />
En size-<strong>change</strong> graf ¡£¢¥¤§¦©¨<br />
fra funktionen ¤<br />
til funktionen ¨ er en bipartit<br />
graf fra parametre til ¤<br />
til parametre til ¨ . Knuderne i grafen er parametre til<br />
hhv. ¤ og ¨ , kanterne angivelser af ændringer i værdierne.<br />
Da der ønskes et resultat, der med sikkerhed angiver om et program teminerer<br />
for alle inddata, betragtes kun to typer af sammenhænge, nemlig og = . Disse<br />
betyder med den benyttede ordning henholdsvis at værdien med sikkerhed bliver<br />
mindre for eller at værdien bliver mindre eller forbliver den samme for = .<br />
Hvis ikke en sådan sammenhæng kan påvises, udelades kanten fra size-<strong>change</strong><br />
grafen.<br />
For et funktionskald fra til kan size-<strong>change</strong> grafen ¡ findes som alle de<br />
sammenhænge mellem parametre i og , der opfylder betingelserne for og<br />
=.<br />
To eller flere size-<strong>change</strong> <strong>grafer</strong> kan sammensættes til multi<strong>grafer</strong> der beskriver<br />
sammenhænge mellem parametre over flere funktionskald. Hvis size-<strong>change</strong><br />
4
<strong>grafer</strong>ne for to funktionskald giver anledning til kanter <br />
¦<br />
henholdsvis ¦ og<br />
, vil sammensætningen indeholde <br />
¦¦<br />
sammenhængen . Sammenhænge<br />
der på denne måde bliver forbundet af kanter fra de enkelte size-<strong>change</strong><br />
<strong>grafer</strong> kaldes for tråde, og beskriver sammenhænge og ændringer i parametre<br />
over en given kaldsekvens.<br />
I et program kan der nu for hvert funktionskald findes en size-<strong>change</strong> graf.<br />
Disse <strong>grafer</strong> kan, ved at kombinere sammenhængene fra de enkelte size-<strong>change</strong><br />
<strong>grafer</strong>, sammensættes til size-<strong>change</strong> <strong>grafer</strong> for hele kaldsekvenser.<br />
For at kunne udvide ovenstående pricip til en grafbaseret algoritme til terminationsbestemmelse<br />
indføres også size-<strong>change</strong> grafkomposition (indeholder essentielt<br />
samme information som multi<strong>grafer</strong>). <br />
¡<br />
Kompositionen af to <strong>grafer</strong><br />
indeholdende kanter <br />
¦<br />
henholdsvis<br />
¦<br />
og vil indeholde en kant<br />
og ¡<br />
¦<br />
. Ændringen i værdien er da givet ud fra samme princip som allerede<br />
<br />
introduceret: for værdier der er garanteret mindre og = for værdier der er<br />
mindre eller forbliver den samme.<br />
At der for en kaldsekvens findes en gennemgående tråd fra til i multigrafen<br />
konstrueret ud fra de tilhørende size-<strong>change</strong> <strong>grafer</strong> svarer til, at der i komposi-<br />
tionen af samtlige size-<strong>change</strong> <strong>grafer</strong> svarende til kaldsekvensen findes en kant<br />
. <br />
<br />
¦<br />
3.2 Uendelige kaldsekvenser<br />
Der benyttes transitiv closure til at finde alle <strong>grafer</strong>, der beskriver uendelige<br />
kaldsekvenser (der er højst endeligt mange <strong>grafer</strong>). Metoden går ud på at oprette<br />
en mængde af size-<strong>change</strong> <strong>grafer</strong> for alle funktionskald, der kan nås fra<br />
indgangen af programmet. I denne mængde inkluderes også alle mulige kompositioner<br />
af disse, dvs. for <strong>grafer</strong>ne ¡¢¤¦¨ <br />
¢¨¦<br />
og i mængden tilføjes<br />
også kompositionen ¡<br />
i mængden. <br />
Herefter kan alle uendelige kaldsekvenser findes ved at undersøge om en given<br />
size-<strong>change</strong> graf i mængden er et fixpunkt. Fixpunkterne er size-<strong>change</strong> <strong>grafer</strong><br />
¡¢¤¦ ¤ hvor kompositionen ¡<br />
¡¡<br />
<br />
. Disse beskriver netop uendelige<br />
kaldsekvenser som defineret i [POPL01].<br />
Hvis alle fixpunkter har en nedadstigende sammenhæng i variable, size-<strong>change</strong><br />
terminerer programmet.<br />
5
4 Et imperativt programmeringssprog<br />
Den i afsnit 5 præsenterede analyse er baseret på et imperativt sprog defineret<br />
ud fra overvejelser omkring imperative egenskaber, funktionalitet i forbindelse<br />
med eksperimentel afprøvning samt simpelhed i beskrivelsen.<br />
4.1 Syntaks<br />
Sproget har følgende syntaks 2 :<br />
<br />
¢¢ <br />
<br />
<br />
¢¢<br />
x y <br />
¢¢<br />
x y <br />
<br />
¢¢ <br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
pred<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
¢¢<br />
<br />
<br />
<br />
else<br />
<br />
<br />
<br />
¢¢ <br />
<br />
<br />
¢¢ <br />
if<br />
<br />
<br />
<br />
<br />
<br />
<br />
while<br />
<br />
<br />
<br />
skip<br />
<br />
<br />
<br />
<br />
return<br />
<br />
<br />
Alle navne på procedurer og variable er forskellige, og <br />
4.2 Egenskaber<br />
<br />
<br />
<br />
<br />
<br />
.<br />
Vigtigst ved sproget er dets imperative kendetegn; disse er en iterativ kontrolstruktur<br />
(while) samt tildelinger, dog med begræsning til globalt tilgængelige<br />
variable. Sproget indeholder procedurekald af to grunde: For det første tillader<br />
det lidt mere interessante testprogrammer, og for det andet kan testprogrammer<br />
derved i højere grad komme til at ligne de tilsvarende funktionelle programmer.<br />
Der er, for simpelhed i semantikkens skyld, dog den begrænsning i sproget, at<br />
mens man kan overføre parametre til procedurekald (de bliver derved ’lokale’<br />
variable i kaldet), er det kun muligt at tilskrive disse i selve kaldet, ikke i procedurens<br />
krop.<br />
Dermed opererer vi med to former for lager: Et store og et environment. Store<br />
indeholder globale variable; symbolerne i dette vil være uændrede gennem hele<br />
programkørslen. Environment indeholder lokale parametre. Environment oprettes<br />
ved indgangen til en procedure og bortkastes ved udgangen af samme, og<br />
symbolernes tilknyttede værdier vil ikke ændres undervejs. Mængden af stores<br />
er og mængden af environments er .<br />
Ligesom procedurekald er tilføjet for funktionalitet til interessante testprogrammer,<br />
er betingede konstruktioner også medtaget. Procedurekald tillades desuden<br />
at være rekursive. Denne funktionalitet tillader at testprogrammer fra funktionel<br />
analyse lettere kan oversættes og resultaterne sammenlignes. Rekursion<br />
medfører dog nogle komplikationer, se afsnit 5.7.<br />
2 I den udviklede implementation forudsættes kommandoer nummererede, men dette fremgår<br />
for overskuelighedens skyld ikke her. Se afsnit 5.1<br />
6
4.3 Forenklende begrænsninger<br />
For simpelheds skyld er værdier begrænset til de naturlige tal. Dette skyldes at<br />
de naturlige tal er tilstrækkelige til at illustrere tanken i analysen, der blot skal<br />
have et fladt domæne med en velfunderet ordning og en dekonstruktør. Vi har<br />
da således en umiddelbar velfunderet ordning på værdier, og sprogets eneste<br />
dekonstruktør er forgængeroperatoren pred.<br />
For yderligere forenkling har vi valgt at begrænse os til kun at tillade simple<br />
udtryk for parametre ved procedurekald for på denne måde ikke at ændre det<br />
globale store i forbindelse med oprettelsen af den kaldte procedures environment.<br />
For at simplificere kontrol-flowet kan procedurer kun forlades efter evaluering<br />
af hele proceduren (derfor optræder den specielle kommando return altid som<br />
sidste kommando). Da vi ikke tillader udtryk at være procedurekald indfører vi<br />
desuden procedurekald ved en tildeling. Dette giver også en god mulighed for<br />
at estimere sammenhænge ved returnering fra proceduren.<br />
4.4 Semantik<br />
Semantikken er som følger:<br />
<br />
<br />
<br />
<br />
<br />
hvor modellerer fejltilstande.<br />
<br />
<br />
¢ <br />
<br />
§<br />
<br />
¦ ¢<br />
¦ §¢<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
¢<br />
¦<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
givet<br />
<br />
<br />
og<br />
<br />
<br />
<br />
<br />
<br />
hvis<br />
<br />
<br />
<br />
<br />
<br />
hvis<br />
<br />
<br />
<br />
§<br />
pred<br />
ellers.<br />
<br />
<br />
hvis<br />
<br />
§ <br />
<br />
<br />
Den semantiske funktion for et program er følgende:<br />
Hvis <br />
<br />
<br />
<br />
<br />
<br />
hvis<br />
<br />
<br />
<br />
er defineret som <br />
<br />
<br />
<br />
<br />
<br />
return<br />
<br />
<br />
<br />
<br />
<br />
<br />
er<br />
hvis<br />
<br />
<br />
<br />
7
hvor<br />
og<br />
if<br />
Lad <br />
<br />
hvor <br />
<br />
<br />
skip <br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
else <br />
<br />
<br />
while<br />
<br />
<br />
<br />
1. Hvis <br />
<br />
<br />
<br />
<br />
og<br />
<br />
<br />
<br />
¦ <br />
¦<br />
<br />
<br />
<br />
<br />
<br />
<br />
hvis<br />
<br />
<br />
og<br />
<br />
<br />
<br />
<br />
<br />
hvis<br />
<br />
<br />
og<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
hvis<br />
<br />
<br />
og<br />
<br />
<br />
<br />
<br />
hvis<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
og<br />
<br />
<br />
<br />
<br />
<br />
<br />
return <br />
<br />
¢<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
og<br />
<br />
hvor<br />
<br />
<br />
¦<br />
<br />
¦<br />
<br />
<br />
siger vi, at terminerer med resultat for <br />
inddata<br />
og skriver<br />
.<br />
<br />
<br />
<br />
2. Hvis på noget tidspunkt evalueringen af et udtryk <br />
værdi i <br />
siger vi at terminerer med <br />
. <br />
Hvis ingen af de ovenstående tilfælde gælder, siger vi, at ikke terminerer, og<br />
<br />
. Vi vil udvide notationen til at omfatte udsagn om for alle<br />
inddata, så eksempelvis:<br />
skriver <br />
<br />
<br />
<br />
<br />
¢ <br />
<br />
.<br />
resulterer i en<br />
5 <strong>Size</strong>-<strong>change</strong> terminationsanalyse af et imperativt<br />
programmeringssprog<br />
Dette kapitel beskriver fremgangsmåden for size-<strong>change</strong> terminationsanalyse af<br />
sproget præsenteret i afsnit 4.<br />
I et funktionelt sprog ændres lageret kun ved funktionskald. I et imperativt<br />
sprog, derimod, kan lageret ændres vilkårlige steder i programkoden. Vi må<br />
derfor tage udgangspunkt i disse steder, som vi vil kalde programpunkter.<br />
Da vi kun betragter ændringer af lageret, vil den her angivne fremgangsmåde<br />
i visse tilfælde føre til det resultat, at et program size<strong>change</strong><br />
terminerer, hvor det ikke er tilfældet. Et program indeholdende<br />
kommandoen terminerer åbenlyst ikke, men da<br />
der ikke forekommer programpunkter i konstruktionen, vil metoden<br />
ikke opdage dette. Problemet at finde “tomme løkker” er imidlertid trivielt,<br />
og vi vil ikke behandle det yderligere.<br />
Idet vi ikke beskæftiger os med datastrukturer (f.eks. lister og træer) og vores<br />
estimat af størrelsændringer dermed begrænser sig til et åbenlyst estimat over<br />
værdier, er det en forholdsvis simpel sag at opstille size-<strong>change</strong> <strong>grafer</strong>.<br />
8
Det er mere kompliceret at beskrive mulige uendelige beregninger. Vi vil naturligt<br />
nok tage udgangspunkt i programpunkterne og beskrive uendelig beregning<br />
som en følge af programpunkter kaldet en kaldsekvens. Størrelsesændringer<br />
over en hel programudførelse kan beskrives ved at sammensætte size-<strong>change</strong><br />
<strong>grafer</strong>ne for de relevante programpunkter.<br />
De følger af programpunkter, der beskriver mulige programudførelser, findes<br />
ved at betragte flow-grafen for et program. Da der er et endeligt antal programpunkter,<br />
vil mindst et programpunkt optræde uendeligt mange gange i en følge,<br />
der beskriver uendelig beregning. Kaldsekvensen for en uendelig beregning må<br />
altså fra et vist punkt bestå udelukkende af sekvenser af programpunkter, der<br />
svarer til cykler i flow-grafen.<br />
5.1 Programpunkter<br />
En tildelingskommando er en kommando af formen <br />
eller <br />
<br />
<br />
<br />
<br />
. Vi antager, at tildelingskommandoerne i et program er num-<br />
<br />
mererede. En tilstandsændring er en <br />
¦<br />
funktion . Et programpunkt<br />
er en tilstandsændring associeret med en tildelingskommando. Således vil kommandoen<br />
<br />
fastlægge et ( programpunkt, noteret for assignment), der beskriver effekten<br />
af at evaluere kommandoen. En kommando af formen<br />
<br />
¢ <br />
<br />
¢ <br />
<br />
<br />
<br />
<br />
<br />
fastlægger to programpunkter, der noteres hhv. (call) og (return). beskriver<br />
bindingen af formelle parametre til procedurekaldet i et environment. <br />
beskriver tilskrivningen af returværdien til det globale store.<br />
5.2 <strong>Size</strong>-<strong>change</strong> <strong>grafer</strong><br />
<br />
<br />
I det funktionelle tilfælde beskriver en size-<strong>change</strong> graf for et givent funktionskald<br />
parametrene relativt til inddata til den kaldende funktion. For et imperativt<br />
sprog beskriver en size-<strong>change</strong> graf for et givent programpunkt tildelingsvariablen<br />
relativt til det globale store samt procedurens environment; for procedurekald<br />
kan de enkelte parametre ses som tildelingsvariablen for det enkelte parameterudtryk.<br />
En size-<strong>change</strong> graf involverer ét programpunkt og beskriver størrelsesændringen<br />
i variable i forbindelse med programpunktet. Knuderne i grafen er variable<br />
i lageret, og kanterne er som i det funktionelle tilfælde sammenhænge mellem<br />
disse.<br />
5.2.1 Sikker estimering af værdier<br />
Vi har i denne rapport begrænset os til at se på et sprog hvor variable og parametre<br />
kun antager værdier i . Med den sædvanlige ordning på giver dette<br />
en naturlig udmåling af størrelser i variable og parametre.<br />
Sproget er defineret med kun en destruktør, men har desuden sammenligningsoperatoren<br />
og additionsoperatoren . Da vi ønsker at verificere termination<br />
9
for alle inddata, ønsker vi kun at repræsentere oplysninger vi med sikkerhed<br />
kan fastslå. Streng nedadstigning i en variabel , hvis nye værdi afhænger af<br />
en variabel , skrives <br />
¦ . Vi estimerer nedadstigning som forekommende<br />
netop hvis det udtryk, der tilskrives , er af formen<br />
hvor kan være en formel parameter eller en global variabel.<br />
<br />
<br />
¢¢<br />
pred <br />
pred<br />
<br />
<br />
<br />
<br />
Vi skriver =<br />
¦<br />
¦<br />
hvis værdien af tilskrives uforandret, og<br />
=<br />
ikke ændrer værdi (herunder hvis ikke optræder i tildelingskommandoen).<br />
hvis <br />
For udtryk bestående af og/eller kan vi ikke med sikkerhed sige noget ¦ om<br />
. Hvis værdien<br />
<br />
af<br />
værdien af tildelingsvariablen, og vi vil her skrive<br />
afhænger af flere variable vil der altid være en sådan usikker overgang.<br />
Eksempel på size-<strong>change</strong> <strong>grafer</strong><br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Ovenstående procedure giver anledning til følgende fire size-<strong>change</strong> <strong>grafer</strong><br />
<br />
= ¦ <br />
¦<br />
<br />
<br />
¦ <br />
<br />
<br />
=<br />
<br />
<br />
¦ <br />
¦ <br />
¦ <br />
<br />
<br />
<br />
=<br />
¦ <br />
<br />
¦ <br />
<br />
<br />
¦<br />
<br />
<br />
<br />
=<br />
¦ <br />
<br />
<br />
<br />
¦<br />
<br />
<br />
<br />
= ¦<br />
<br />
<br />
<br />
Bemærk, at ikke indgår i den sidste graf. Dette skyldes, at er lokal for <br />
, og<br />
=<br />
<br />
= ¦ <br />
derved ikke bevares ved udgangen af proceduren.<br />
5.3 Tråde<br />
=<br />
<br />
<br />
<br />
<br />
¦ <br />
=<br />
<br />
=<br />
<br />
=<br />
<br />
<br />
= ¦ <br />
<br />
I den funktionelle analyse indeholdt size-<strong>change</strong> <strong>grafer</strong>ne kun information om<br />
parameterevalueringen ved funktionskald. Udover denne binding ved procedurekald<br />
har vi også bindingen ved almindelige tildelinger samt bindingen af returværdier<br />
til en variabel. Da vi benytter os af et globalt variabelrum, må dette<br />
naturligvis influere på genereringen af tråde3 .<br />
En bemærkelsesværdig egenskab ved en tildeling som f.eks. , er at<br />
værdien af ikke ændres over evalueringen af tildelingen. Hvis er en parameter<br />
til proceduren indeholdende <br />
vil size-<strong>change</strong> grafen for programpunktet<br />
give anledning til to sammenhænge, den ud fra tildelingen åbenlyse = ¦<br />
og <br />
bevarelsen af vores © environment = ¦<br />
. <br />
På samme måde skal der tages højde for overskrivning af vædier, så en senere<br />
tildeling overskriver den tidligere sammenhæng med en ny. Hvis vi tilføjede programpunktet<br />
<br />
, er det klart at der ikke ville forekomme en gennem-<br />
<br />
3Tråde er defineret som i afsnit 3.<br />
10<br />
=
gående sammenhæng mellem og , som det var tilfældet ved programpunkt<br />
<br />
Sammensætningen af size-<strong>change</strong> <strong>grafer</strong> samt genkendelse af tråde heri er altså<br />
ikke væsentligt anderledes end i den funktionelle analyse.<br />
En nedadstigende tråd er en tråd, der højst har endeligt mange usikre overgange<br />
<br />
¦ og uendeligt mange nedadgående overgange <br />
¦ .<br />
5.4 Returværdier<br />
I [POPL01] tages der ikke hensyn til funktioners returværdier. [Frederiksen]<br />
benytter to algoritmer til udtrækning af size-<strong>change</strong> <strong>grafer</strong>; den ene tager hensyn<br />
til returværdier og den anden gør ikke. Konklusionen heri er, at det er i de<br />
færreste tilfælde, information om returværdier spiller nogen rolle for analysen.<br />
Tilstedeværelsen af globale variable i et imperativt programmeringssprog ændrer<br />
imidlertid på dette. Betragt følgende program:<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Der er kun én mulig uendelig kaldsekvens i dette program, og den indeholder<br />
en nedadstigende tråd i <br />
, altså size-<strong>change</strong> terminerer programmet. Hvis vi<br />
<br />
ikke kigger på returværdier kan vi imidlertid ikke foretage et bedre estimat end<br />
¦ <br />
ved tildeligskommandoen 1, da<br />
<br />
er en global variabel og som<br />
<br />
sådan kan ændre værdi under evalueringen af . Det er klart, at en så triviel<br />
tilføjelse som proceduren helst ikke burde påvirke resultatet af terminationsanalysen,<br />
hvorfor vi er nødt til at inkludere returværdier i analysen.<br />
5.5 Kaldsekvenser<br />
En uendelig udregning er i vores analyse repræsenteret ved en uendelig sekvens<br />
af programpunkter. Vi har tidligeret set, hvordan size-<strong>change</strong> <strong>grafer</strong> for programpunkter<br />
estimeres, samt hvordan flere size-<strong>change</strong> <strong>grafer</strong> sammensættes til<br />
en multigraf, der beskriver sammenhængen over en sekvens af programpunkter.<br />
Ved at benytte flowanalyse kan vi lokalisere uendelige udregninger i vores<br />
program, kombinere size-<strong>change</strong> <strong>grafer</strong>ne for de respektive programpunkter og<br />
hermed afgøre om der eksisterer en nedadstigende tråd.<br />
5.5.1 Flowanalyse<br />
De mulige sekvenser i programmet findes ved for hvert program punkt at se<br />
hvilke programpunkter der kan nås i et enkelt beregningsskridt. Hvis den efterfølgende<br />
sætning er en tildeling vil det efterfølgende <br />
programpunkt være eller<br />
11
for henholdsvis variabeltildelinger eller procedurekald. Ved betingede sætnin-<br />
<br />
ger som if-else og while vil både programpunktet for sand og falsk evaluering<br />
være potentielle efterfølgere. Efterfølgende programpunkter til procedurekald<br />
) vil være første programpunkt i den kaldte procedure.<br />
(<br />
Hvis et programpunkt i proceduren efterfølges af returnering fra <br />
, er det<br />
efterfølgende programpunkt tildelingen ved procedurekaldet<br />
<br />
til . F.eks.<br />
vil et kald til en procedure<br />
<br />
<br />
¦ .<br />
Vi kan udfra ovenstående fremgangsmåde nu finde sammenhængen mellem<br />
give anledning til sammenhængen <br />
programpunkter og deres potentielle efterfølgere.<br />
Ved at lade programpunkterne være knuder og tilføje kanter til potentielle ef-<br />
<br />
terfølgende programpunkter, kan vi lave en flow-graf for programmet. Program-<br />
punkterne og <br />
forbindes med en kant fra til netop hvis kan nås fra i et<br />
beregningskridt.<br />
Alle mulige uendelige kaldsekvenser i programmet kan nu findes ved at betragte<br />
cykler (svarende til sekvenser af programpunkter) i flowgrafen.<br />
5.6 Uendelige kaldsekvenser - simple og kombinerede cykler<br />
Fremgangsmåden i lokaliseringen af cykler i flowgrafen tager udgangspunkt<br />
i lokalisering af simple cykler heri. En simpel cyklus er en cyklus hvori alle<br />
programpunkter er forskellige. I en flowgraf kan alle sådanne findes ved en<br />
dybde-først søgning. Det er ikke tilstrækkeligt kun at kigge på simple cykler, da<br />
to simple cykler, begge med en nedadstigende tråd, tilsammen kan danne en<br />
cyklus (dog ikke simpel) uden en nedadstigende tråd. Et eksempel på dette kan<br />
ses nedenfor.<br />
Den simpleste løsning på dette problem er at undersøge enhver mulig kombination<br />
af de simple cykler, der har programpunkter til fælles. Antallet af kombinationer<br />
er eksponentielt i antallet af simple cykler, hvorfor vi her har en mulig<br />
“kombinatorisk eksplosion” i algoritmen (det viser sig at være et reelt problem i<br />
praksis, se afsnit 7).<br />
12
Eksempel<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Her er to simple cykler, ¦ ¦ og ¦ ¦ . I den første cyklus er<br />
der en nedadstigende tråd i , og i den anden en nedadstigende tråd i .<br />
Imidlertid giver den kombinerede cyklus ¦ ¦ ¦ ¦ ¦ <br />
også anledning til en mulig uendelig kaldsekvens, men denne indeholder ingen<br />
nedadstigende tråd 4 .<br />
5.7 Realiserbare kaldsekvenser<br />
Ved genkendelse af uendelige kaldsekvenser, opstår der et problem. Det sker<br />
i forbindelse med en uendelig sekvens af returneringer fra en procedure. Det<br />
er intuitivt klart, at man ikke kan returnere fra en procedure uden først at<br />
have kaldt den, men dette fanges ikke umiddelbart af vores metode. En uendelig<br />
kaldsekvens, der indeholder både kald og returneringer eller kun kald,<br />
kan imidlertid sagtens forekomme.<br />
Betragt nedenstående program:<br />
¥<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Flowgrafen for dette program er vist i figur 1. Der ses at være to cykler i flowgrafen:<br />
¦ <br />
og <br />
¦ . Den første indeholder en nedadstigende tråd (i<br />
<br />
),<br />
<br />
men den anden gør ikke, hvorfor vi må konkludere, at programmet ikke size<strong>change</strong><br />
terminerer. Imidlertid vil en kaldsekvens der består af uendeligt mange<br />
overgange ¦ aldrig kunne forekomme.<br />
<br />
4 Bemærk, at programmet terminerer, men ikke size-<strong>change</strong> terminerer.<br />
13
1<br />
c<br />
3 c<br />
2 a<br />
Figur 1: Flowgraf<br />
Problemet løses ved en fremgangsmåde beskrevet i [Reps 98]. At finde kaldsekvenser<br />
vha. en flowgraf er ikke præcist nok, så vi må sortere yderligere i de<br />
kaldsekvenser, vi opdager. Pointen er kun at medtage kaldsekvenser, hvor hver<br />
returnering fra en procedure tilsvarer et kald til samme procedure.<br />
For et procedurekald forekommende ved kommandoen mærkes de tilhørende<br />
programpunkter med parenteser: <br />
<br />
markeres med og <br />
tilsvarende bliver<br />
markeret med 5 . Kaldsekvenser, der kan forekomme (kaldet realiserbare<br />
<br />
kaldsekvenser),<br />
er da dem, for hvilke den tilsvarende sekvens af parenteser udgør<br />
et ord i sproget defineret ved følgende kontekstfri grammatik:<br />
<br />
<br />
<br />
<br />
<br />
¢¢ <br />
<br />
<br />
¢¢ <br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
1 r<br />
3 r<br />
<br />
<br />
<br />
<br />
Genkendelse af kontekstfri sprog er imidlertid ikke trivielt. Vi bemærker dog,<br />
at enhver kaldsekvens, der indeholder flere højreparenteser, , end venstrepa-<br />
<br />
, for et givent , er ugyldig. Vi vil da bruge dette simplere kriterium<br />
<br />
renteser,<br />
for realiserbare kaldsekvenser, da det løser det i eksemplet ovenfor skitserede<br />
problem6 Vores drøftelse af realiserbare kaldsekvenser er ikke komplet, men da<br />
den simple løsning er brugbar, vil vi ikke behandle emnet yderligere.<br />
5 Begrebet realizable i [Reps 98] er her oversat til realiserbar.<br />
6 Som det vises i afsnit 7, giver denne indskrænkning os ikke problemer i praksis.<br />
14
1<br />
2<br />
a<br />
a<br />
3<br />
a<br />
a<br />
4<br />
Figur 2: Flowgraf for <br />
5.8 Eksempel på size-<strong>change</strong> analyse<br />
Betragt følgende eksempel:<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Der konstrueres en flowgraf, som viser de måder hvorpå programmet kan afvik-<br />
les. For ser den således ud<br />
De tilsvarende size-<strong>change</strong> <strong>grafer</strong> findes ud fra programpunkterne og flowgrafen.<br />
Det resulterer i flg. size-<strong>change</strong> graf<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
¦ <br />
=<br />
=<br />
<br />
¦<br />
<br />
¦ ¦ <br />
<br />
=<br />
<br />
¦<br />
<br />
<br />
¦ <br />
<br />
<br />
hvor vi kun har medtaget variable, der indgår direkte ved de respektive programpunkter.<br />
Som det fremgår af flowgrafen, er der kun én mulig uendelig<br />
kaldsekvens, nemlig den der modsvarer -løkken indeholdende programpunkter<br />
<br />
og .<br />
Ved at undersøge size-<strong>change</strong> <strong>grafer</strong>ne for disse overgange kan vi sammen sætte<br />
dem til tråde. Det fører til flg. tråde<br />
=<br />
Nr. Tråd ¦ <br />
<br />
¦<br />
<br />
15<br />
6<br />
5<br />
a<br />
a
Som det ses, er der i denne løkke en nedastigende tråd (nr. 2), og dermed kan<br />
det konkluderes, at programmet size-<strong>change</strong> terminerer.<br />
6 Implementation af size-<strong>change</strong> terminationsanalyse<br />
For at kunne foretage praktiske eksperimenter er en implementation af den i<br />
afsnit 5 præsenterede metode udviklet.<br />
Der er selvfølgelig lagt vægt på korrektehed i implementationen, men overvejelser<br />
om effektivitet og god programmeringsstil har ikke indgået; en såkaldt<br />
quick-and-dirty implementation.<br />
Programmet er skrevet i ML og er opbygget af følgende moduler:<br />
Parser En parser, genereret med ML-YACC.<br />
Flow<strong>grafer</strong> Opbygning af flow<strong>grafer</strong> sker ved for et hvert programpunkt<br />
at finde de programpunkter der kan nås i et beregningsskridt.<br />
Cykler De simple cykler i flowgrafen findes ved en dybde-først søgning.<br />
Desuden indeholder modulet funktionerne til eliminering af ikke mulige<br />
cykler, se afsnit 5.7.<br />
<strong>Size</strong>-<strong>change</strong> <strong>grafer</strong> For programmet estimeres for hvert programpunkt<br />
den tilhørende size-<strong>change</strong> graf. (Nedadstigende sammenhænge findes<br />
netop ved udtryk kun indeholdende pred operatoren, værdier i udtryk<br />
der involverer de øvrige operatorer kan ikke med sikkerhed beskrives).<br />
Modulet indeholder funktioner til at finde tråde i en given kaldsekvens.<br />
Verifikation Modulet undersøger, om der for enhver cyklus eksisterer en<br />
nedadstigende tråd. Efter undersøgelse af de simple cykler undersøges alle<br />
mulige kombinationer af cykler med fælles programpunkter. Hvis verifikationen<br />
af en cyklus fejler, afbrydes med en udskrift af den funde cyklus.<br />
Ved programmer der ikke size-<strong>change</strong> terminerer returneres altså kun den<br />
først fundne cyklus uden en nedadstigende tråd. Hvis alle cykler indeholder<br />
en nedadstigende tråd, returneres samtlige fundne mulige cykler.<br />
7 Eksperimentelle resultater<br />
7.1 Testprogrammer<br />
Det udviklede program verificeres ved afprøvning med en række test-suiter,<br />
der skal klarlægge termination for eksempelprogrammer. For at have en reference<br />
at sammenligne resulteter med, hentes disse test-suiter fra kendte kilder.<br />
Der er her tale om [POPL01] og dele af Wahlstedt og Glenstrup suiterne fra<br />
[Frederiksen].<br />
Der er dog problemer i dette, idet disse programmer ofte benytter lister, en<br />
datatype der ikke findes i vores sprog. Løsningen består i at transformere programmerne<br />
til en lignende funktionalitet der benytter heltal. Som eksempel kan<br />
nævnes equal funktionen. Hvor den før testede om to lister var ens (indeholdt<br />
de samme elementer), tester den nu om to tal er ens. Det tilstræbes at holde<br />
virkemåden så tæt på det oprindelige program som muligt. Det hænder at de<br />
16
transformerede programmer ikke udfører noget meningsfyldt, f.eks. tæller sine<br />
argumenter ned og returnere 0 i alle tilfælde.<br />
Idet [Frederiksen] beskæftiger sig med funktionsbaseret terminationsanalyse<br />
over funktionskald, tilstræbes det at de transformerede programmer så vidt<br />
muligt benytter imperative konstruktioner, især -løkker. Det er ikke altid<br />
muligt at lave en sådan omskrivning og bevare programmerne tæt nok op ad<br />
hinanden til at de er sammenlignelige, og hvor dette er tilfældet har vi brugt en<br />
mere funktionsbaseret tilgang, dvs. benyttelse af procedurekald og rekursion.<br />
Testprogrammerne er alle oversat i hånden.<br />
7.1.1 Jones<br />
Denne suite indeholder 5 programmer, taget fra [POPL01]. At det ene program<br />
er udeladt (den oprindelige suite indeholdt 6 programmer) skyldes at det ikke<br />
kunne lade sig gøre lave et tilsvarende program i vores sprog. Det udeladte<br />
program lavede liste-reversion.<br />
Bemærkninger til suiten<br />
Eksemplerne i denne testsuite terminerer alle af en simpel grund. Alle programmerne<br />
består af rekursive procedurekald, hvor der ved hvert kald er en streng<br />
nedgang i en af parametrene. Denne nedgang kan udtrækningen af size-<strong>change</strong><br />
<strong>grafer</strong>ne let detekterer og dermed vil det bliver markeret som en nedadstigende<br />
tråd i den løkke der kommer til at repræsentere kaldet.<br />
7.1.2 Wahlstedt<br />
Wahlstedet eksemplerne er taget fra [Frederiksen] og oprindeligt designet af<br />
David Wahlstedt [Wahlstedt]. Af eksemplerne i [Frederiksen] er udvalgt 6 programmer<br />
der bl.a. udfører diverse aritmestriske funktioner (addition, division<br />
o.s.v)<br />
Bemærkninger til suiten<br />
Mange af disse eksempler terminerer af samme grund som Jones eksemplerne,<br />
nemlig fordi der er tale om procedurekald med en skarp dekrementering af en<br />
eller flere parametre. Der er dog et par som skiller sig ud.<br />
Wexadd Dette program terminerer fordi der i while løkken er en skarp nedgang<br />
i betingelsesvariablen. Dermed afsluttes løkken og da programmet ikke<br />
har andre potentielt uendelige kaldsekvenser, terminerer hele programmet.<br />
Wexfgh Dette program size-<strong>change</strong> terminerer ikke. Det skyldes at vi har en<br />
serie af tildelinger som ikke giver os sammenhængende information om de variables<br />
relative størrelser. Den serie af tildelingerne der er tale om er 8, 9, 6, 3,<br />
4, 3, 8, 2. I denne kaldsekvens er der ganske vist adskillige skarpe nedgange,<br />
men da der ved enkelte kald lægges et tal til parametrene, kan vi ikke med sikkerhed<br />
sige noget om deres størrelse. Dette forhindrer size-<strong>change</strong> analysen i at<br />
bestemme termination.<br />
17
Det kan dog ses at programmet vil terminere på trods af dette. Dette skyldes<br />
at additionen i tildeling 9 (b+1) ikke influerer på returværdien af programmet,<br />
idet det vi returnere 0 uanset hvad b er (idet a vil være nul). Ved næste kald<br />
bliver b derfor 0 (den er ikke blevet ændret i det foregående kald) og dermed<br />
terminerer programmet efter tildeling 2.<br />
7.1.3 Glenstrup<br />
Glenstrup eksemplerne er oprindeligt konstrueret af Arne Glenstrup [Glenstrup],<br />
men er her taget fra [Frederiksen]<br />
To programmer kræver en særlig kommentar. De er ikke hentet fra andre kilder<br />
som de øvrige, men er nogle vi har lavet selv. Der er tale om Gexgcdimp og Gexpermute2.<br />
Gexgcdimp er en iterativ version af Gexgcd. Gexpermute2 er lavet<br />
for at demonstrerer hvorledes et program kan gå galt ved forkert brug af en global<br />
variabel. Gexpermute genbruger én variabel, og som det fremgår nedenfor,<br />
bevirker det at programmet ikke terminerer. Bruger man derimod to forskellige<br />
variable, vil programmet size-<strong>change</strong> terminere. Det skal bemærkes at Gexpermute<br />
ikke har den samme funktionalitet som den oprindelige funktion, hvorfor<br />
resultaterne med [Frederiksen] for dette program ikke kan sammenlignes.<br />
Bemærkninger til suiten<br />
De programmer som terminerer her skyldes alle enten et rekursivt kald med en<br />
direkte nedgang i et af parametrene eller en while løkke med en stadigt faldende<br />
betingelse. 3 programmer i suiten size-<strong>change</strong> terminerer ikke, Gexincr, Gexspl<br />
og Gextrick.<br />
Gexspl og Gextrick Begge disse programmer har et rekursivt kald hvori der<br />
ikke er en skarp nedgang. I Gexspl er det ved tildeling 5 og i Gextrick er det<br />
sekvensen 2, 5, 6 der afstedkommer problemet. Grunden er i Gextrick at ved<br />
at dele beregningerne over to betingede sætninger mister vi sammenhængen<br />
imellem dem.<br />
Gexgcdimp Dette program blev konstrueret for at undersøge om det ville være<br />
muligt at bestemme size-<strong>change</strong> termination for gcd ved brug af en iterativ metodik<br />
istedet for den rekursive. Dette viste sig ikke at være tilfældet. Grunden<br />
skal søges i sammenhængen mellem parametre og returværdier. Under kald til<br />
hjælpeprocedurer kan vi miste sammenhængen mellem disse, f.eks. hvis vi returnerer<br />
en værdi, der ikke har nogen relation til de parametre proceduren blev<br />
kaldt med. En sådan situation er kun mulig på grund af eksistensen af globale<br />
variable.<br />
Testprogram antal simple løkker SCT? SCT? fra [Frederiksen]<br />
Jex2 2 Ja Ja<br />
Jex3 8 Ja Ja<br />
Jex4 6 Ja Ja<br />
Jex5 6 Ja Ja<br />
Jex6 4 Ja Ja<br />
Wexadd 1 Ja Ja<br />
18
Wexdiv2 2 Ja Ja<br />
Wexeq 2 Ja Ja<br />
Wexfgh 1165 Nej Nej<br />
Wexoddeven 2 Ja Ja<br />
Wexpermut 2 Ja Ja<br />
Gexbinom 10 Ja Ja<br />
Gexdecr 1 Ja Ja<br />
Gexgcd 23 - Ja<br />
Gexgcdimp 18 Nej -<br />
Gexincr 1 Nej Nej<br />
Gexmatch 6 Ja Ja<br />
Gexnestdec 2 Ja Ja<br />
Gexnolexicord 6 Ja Ja<br />
Gexpermute 5 Nej -<br />
Gexpermute2 5 Ja -<br />
Gexspl 10 Nej Nej<br />
Gextrick 5 Nej Nej<br />
7.2 Opsummering af resultater<br />
Som det fremgår af tabellen følger resultaterne af vores test meget godt de<br />
resultater som [Frederiksen] har opnået med size-<strong>change</strong> termination, hvor der<br />
tages hensyn til returværdier. Dette er forventeligt, idet programmerne har en<br />
lignende opbygning.<br />
Det er ikke lykkedes at afgøre om Gexgcd size-<strong>change</strong> terminerer. Kombinationen<br />
af simple cykler giver eksponentielt mange cykler, og da Gexgcd indeholder<br />
23 simple cykler, har det ikke været muligt at afgøre termination grundet det<br />
store hukommelsesforbrug. At vi ikke kan afgøre size-<strong>change</strong> termination for<br />
Gexgcd er ikke et udtryk for at den udviklede algoritme ikke er korrekt, men<br />
dog at den ikke er effektiv.<br />
Til sammenligning med Gexgcd kan der kigges på Wexfgh, som har et meget<br />
stort antal simple cykler. Denne terminerer hurtigt, med negativt resultat, men<br />
det skyldes netop at der i den findes en simpel cyklus uden nedadstigende tråd,<br />
hvilket betyder at vi kan afgøre termination uden at checke alle kombinationerne<br />
af de 1165 simple cykler. Det samme er tilfældet med Gexgcdimp.<br />
Denne eksponentielle eksplosion er et oplagt punkt til forbedring. Det viser sig<br />
i praksis at vores metode, selv for små programmer med nedadstigende tråde i<br />
alle simple cykler, ikke kan bestemme termination indenfor overkommelig tid.<br />
At den mangel på sammenhæng mellem parametre og returværdier vi oplever i<br />
Gexgcdimp giver ikke-termination, for et program som vil terminere, skyldes at<br />
vores analyse ikke benytter sammenhængen mellem betingede sætninger og variables<br />
værdier til at udelukke umulige kombinerede cykler. I Gexgcdimp er den<br />
cyklus der resulterer i ikke-termination umulig, fordi betingelsen i proceduren<br />
mul fejler uden at cyklusen bliver udført; en situation der ikke er mulig i den<br />
kontekst hvor proceduren kaldes.<br />
19
8 Konklusion<br />
Vores analyse adskiller sig væsentligt fra den funktionelle på følgende punkter.<br />
Det er nødvendigt at indføre den mere omfattende definition på programpunkter<br />
da vi tillader ændringer i lageret andre steder end ved procedurekald.<br />
Vi må, for lokalisering af uendelige kaldsekvenser, anvende metoder indenfor<br />
flowanalyse, herunder flow<strong>grafer</strong> og genkendelse af kontekstfire<br />
sprog.<br />
Tilstedeværelsen af de globale variable tvinger os til at se og udmåle sammenhængene<br />
i returværdier, som i [Frederiksen].<br />
Processen ved lokalisering af alle mulige kaldsekvenser, er mere omstændelig,<br />
da vores metode ikke bygger på grafkomposition.<br />
Den udviklede analysemetode er mindst lige så stærk som den tilsvarende metode<br />
for funktionelle sprog i [POPL01]. I praktisk afprøvning får vi resultater<br />
tilsvarende dem, der findes i [Frederiksen].<br />
Imidlertid viser implementationen, at metoden er for ineffektiv til behandling af<br />
eksempler i selv moderat størrelse, som illustreret ved testprogrammet Gexgcd.<br />
Til trods for, at hastigheden formentlig kunne forbedres væsentligt ved en mere<br />
effektiv implementation, er analysemetoden dog den egentlige begrænsning.<br />
Problemet er, at antallet af kombinationer af cykler, der skal gennemsøges, er<br />
eksponentielt i antallet af simple cykler. Fremgangsmåden i [POPL01] angiver<br />
en algoritme med eksponentiel tidskompleksitet. Ligeledes vises det her, at size<strong>change</strong><br />
terminationsanalyse er PSPACE-komplet. Vi kan altså ikke gøre os håb<br />
om at finde en metode med bedre worst-case tidskompleksitet. Det betyder dog<br />
ikke, at man ikke kan udvikle en metode med betydelig bedre effektivitet i praksis<br />
end vores.<br />
Forbedringer kunne være:<br />
Undersøgelse af sammenhængen mellem size-<strong>change</strong> <strong>grafer</strong> og kaldsekvenser,<br />
for at afgøre om det er muligt at kombinere cykler på en mere<br />
effektiv måde.<br />
Optimering ved bedre repræsentation af cykler, på en måde så redundansen<br />
fra næsten identiske cykler minimeres.<br />
Bedre opstillling af kaldsekvenser, evt. ved brug af automatteori.<br />
Muligheden for at gøre brug af grafkomposition, i stil med [POPL01], hvilket<br />
ville mindske specielt pladsforbrug.<br />
Vores fremgangsmåde er temmelig ligetil; derfor må vi afsøge mange tilfælde,<br />
der kunne udelukkes på forhånd. Det største problem ved vores fremgangsmåde<br />
er antallet af cykler, der skal kombineres med hinanden. Vi finder ukritisk alle<br />
simple cykler i et program, og det kan være mange hvis programmet indeholder<br />
indlejrede forgreninger. Mange af disse vil dog minde temmelig meget om hinanden<br />
set ud fra et size-<strong>change</strong> synspunkt, men de bidrager ikke desto mindre<br />
til problemets størrelse.<br />
En lovende forbedring af metoden er at søge en bedre måde, hvorpå man kan<br />
opstille kaldsekvenser. Automatteori kunne muligvis være til nytte her.<br />
20
Den umiddelbart mest frugtbare vej at gå er at lede efter en bedre måde at<br />
kombinere cyklerne på, idet man herved ville reducere antallet af kombinationer<br />
uden at skulle løse potientelt svære problemer som flow-analyse e.l.<br />
Ud over den manglende effektivitet er vores fremstilling af size-<strong>change</strong> terminationsanalyse<br />
temmelig uformel. Der ligger et oplagt arbejde i at formulere<br />
metoden stringent matematisk set.<br />
21
Litteratur<br />
[POPL01] Chin Soon Lee m.fl.: The <strong>Size</strong>-Change Principle for Program Termination,<br />
Principles of Programming Languages, 2001<br />
[Winskel] Glynn Winskel: The formal Semantics of Programming Languages,<br />
MIT Press, 1996<br />
[Reps 00] Thomas Reps: Undecidability of Context-Sensitive Data-<br />
Dependence Analysis, ACM Transactions on Programming<br />
Languages and Systems 22, 2000<br />
[Reps 98] Thomas Reps: Program analysis via graph reachability, Information<br />
and Software Technology 40, 1998<br />
[Frederiksen] Carl Christian Frederiksen: A Simple Implementation of the <strong>Size</strong>-<br />
Change Termination Principle, Skriftligt arbejde på DIKU, 2001<br />
[Wahlstedt] David Wahlstedt: Detecting termination using size-<strong>change</strong> in parameter<br />
values, Speciale, Chalmers University of Technology,<br />
2000<br />
[Glenstrup] Arne J. Glenstrup: Terminator II: Stopping Partial Evaluation of<br />
Fully Recursive Programs, Speciale, DIKU, 1999<br />
22
A Testprogrammer<br />
Jex2<br />
<br />
<br />
<br />
¤¦¥ ¤ ¡£¢ <br />
<br />
<br />
¤ <br />
¡£¢¡ ¤ §©¨<br />
<br />
¢ ¨<br />
¦<br />
<br />
¡¡ ¤¥<br />
¨<br />
<br />
<br />
¡ ¤<br />
¦ <br />
<br />
Jex3<br />
<br />
¡ ¤¥ <br />
¤ <br />
<br />
§©¨<br />
¤ <br />
<br />
<br />
¤ ¡ § ¤ ¨<br />
¡ ¤¤<br />
¨ <br />
<br />
<br />
¤ ¡ ¤<br />
¨<br />
<br />
<br />
¦<br />
Jex4<br />
<br />
<br />
¡ ¡ ¤¥ <br />
<br />
<br />
¤ <br />
<br />
¡ <br />
<br />
¤ ¡ ¤<br />
§©¨ <br />
<br />
<br />
¤ <br />
¡ ¤ ¡ ¤<br />
¨<br />
¨<br />
¦<br />
<br />
Jex5<br />
¤¥ ¢¡£<br />
¤<br />
§©¨ ¢<br />
¢ ¤ <br />
¡ <br />
<br />
¤¤<br />
¨ <br />
¡ <br />
<br />
¢ ¤¤<br />
¨<br />
<br />
Jex6<br />
<br />
<br />
¦ <br />
¡ ¤¥ <br />
¤<br />
§©¨<br />
<br />
<br />
<br />
¤ ¡<br />
¨<br />
¦<br />
<br />
¤¥ ©¡<br />
¤ <br />
<br />
<br />
¤ ¡ <br />
<br />
¤¤<br />
<br />
¨ <br />
<br />
<br />
¤ ¡ <br />
<br />
¤ ¤<br />
¨<br />
¦<br />
Wexadd<br />
<br />
<br />
<br />
¢¡ ¤¥ <br />
¤ ¢<br />
§©¨<br />
¥ <br />
¢ ¨<br />
¨ <br />
<br />
<br />
<br />
<br />
¤¤¥<br />
¨<br />
<br />
§ <br />
¨ <br />
<br />
¨<br />
¦ <br />
¤<br />
23
Wexdiv2<br />
<br />
<br />
<br />
<br />
<br />
¤¥ <br />
¤ <br />
§©¨<br />
¥ <br />
<br />
<br />
¤ ¤ <br />
¨ <br />
<br />
<br />
<br />
<br />
<br />
<br />
¤¤¤<br />
¨ <br />
§ ¨<br />
<br />
¦<br />
Wexeq<br />
<br />
¡ ¤¥ <br />
¤ <br />
¤<br />
§©¨§ <br />
¨ <br />
¤ <br />
¨ <br />
<br />
<br />
¤ ¡ <br />
<br />
¤¤<br />
¨<br />
¦<br />
<br />
Wexfgh<br />
<br />
<br />
<br />
<br />
<br />
¡ ¤¥ <br />
¤<br />
§©¨<br />
<br />
<br />
¤ <br />
¨<br />
¥ ¨<br />
<br />
<br />
<br />
<br />
<br />
¤ ¡ ¤ <br />
<br />
¡ ¤¤<br />
¨<br />
¨<br />
<br />
¤<br />
<br />
¡ ¦<br />
<br />
¡ ¤¥ <br />
¤ <br />
¨ <br />
¤ <br />
¨<br />
¥ ¡ ¤<br />
¨<br />
<br />
<br />
¤ ¡ § ¤<br />
¨ <br />
¨<br />
<br />
¡ ¤<br />
§<br />
¦<br />
<br />
¡ ¤¥ <br />
¤<br />
§§©¨<br />
<br />
<br />
¤ <br />
§¦¨ <br />
<br />
<br />
¤ ¤ ¡ §¦¨<br />
Wexoddeven<br />
¦ <br />
¦<br />
<br />
¤¥ <br />
¤ <br />
§©¨§ <br />
<br />
<br />
¤¤<br />
¨<br />
¦<br />
<br />
<br />
¤¥ <br />
¤<br />
¨<br />
<br />
<br />
¨<br />
<br />
<br />
<br />
<br />
<br />
¤¤ <br />
<br />
Wexpermut<br />
¦ <br />
24
¡ ¤¥ <br />
¤ <br />
§©¨ <br />
¡ <br />
<br />
¤¤<br />
¨<br />
¦<br />
<br />
Gexbinom<br />
<br />
<br />
<br />
¡£ ¤¥ <br />
¤ <br />
§©¨<br />
<br />
¤ <br />
¨ <br />
¥<br />
<br />
<br />
<br />
<br />
<br />
¤ ¡ <br />
<br />
¤¤<br />
¨<br />
¨<br />
¨<br />
<br />
<br />
<br />
¦<br />
Gexdecr<br />
<br />
<br />
¢ ¤¥ <br />
¤ ¢<br />
<br />
§©¨<br />
<br />
¢ ¨<br />
¤¥<br />
¨<br />
<br />
<br />
<br />
¤<br />
<br />
<br />
¨<br />
¦<br />
Gexgcd<br />
<br />
<br />
¢¡£ ¤¥ <br />
¤ ¢<br />
¨<br />
<br />
¤ <br />
¨ <br />
<br />
<br />
¢¡£ ¤<br />
¨<br />
¤ <br />
¢ ¨<br />
<br />
¢ ¤¥ <br />
¨ <br />
<br />
¢¡ ¤<br />
<br />
<br />
<br />
¡ ¤ ¨<br />
¥ ¨ ¡£¢ ¤<br />
<br />
<br />
<br />
¡ ¤<br />
¨<br />
¦<br />
<br />
<br />
<br />
¡ ¤¥ ¦<br />
<br />
¤ <br />
¤<br />
§©¨§ <br />
¨<br />
¨<br />
¦<br />
<br />
<br />
¡ ¤¥ <br />
¤ <br />
<br />
¤ ¡ ¤<br />
¨ <br />
<br />
¤ §<br />
<br />
<br />
<br />
<br />
¤ ¡ <br />
<br />
¤¤<br />
§§¨<br />
¦ <br />
25
Gexgcdimp<br />
<br />
<br />
¡ ¤¥ <br />
¤ <br />
¢ §©¨ <br />
¤ <br />
¢ ¨<br />
¥ <br />
¨<br />
¢ ¨<br />
¨ <br />
<br />
<br />
<br />
<br />
¢ ¤¤¥<br />
¨<br />
<br />
<br />
<br />
¢ ¡ ¢ ¤<br />
¢ <br />
¡ ¢ ¤<br />
¨ <br />
<br />
<br />
<br />
<br />
¢ ¡ <br />
<br />
¤<br />
¨<br />
¢ ¢ ¨<br />
§ ¨ ¢ <br />
<br />
<br />
¢ ¦<br />
<br />
<br />
<br />
<br />
¡ ¤¥ <br />
<br />
<br />
<br />
<br />
<br />
¨ <br />
§©¨ <br />
<br />
<br />
<br />
<br />
¤¤¥ <br />
<br />
¦ ¤<br />
¨<br />
<br />
<br />
¤<br />
¨<br />
<br />
<br />
¨ <br />
¦<br />
<br />
¡ ¤¥<br />
§ ¨<br />
<br />
<br />
§¨<br />
¨ <br />
¤ <br />
¤<br />
¤<br />
¨<br />
<br />
<br />
<br />
¤¥ <br />
<br />
¡ ¤<br />
§©¨<br />
§ ¨<br />
<br />
¦<br />
<br />
<br />
¡ ¤¥<br />
¨<br />
<br />
<br />
<br />
¨ <br />
<br />
<br />
<br />
<br />
¤¤¥<br />
¨<br />
<br />
<br />
¨<br />
<br />
¦<br />
Gexincr<br />
<br />
<br />
<br />
<br />
¢ ¤¥ <br />
¤ ¢<br />
§©¨<br />
¢ ¨ <br />
<br />
<br />
¤¥ <br />
<br />
¨ § <br />
¤<br />
26
¦ <br />
Gexmatch<br />
<br />
<br />
¡ ¤¥ §©¨<br />
<br />
¡ ¡ ¡ ¤ ¦ <br />
<br />
<br />
<br />
¡ ¡ ¤¥ ¡ <br />
¤<br />
¨§ <br />
<br />
<br />
¤ <br />
¨<br />
<br />
¤<br />
¨<br />
<br />
<br />
¤ ¡ <br />
<br />
¤ ¡ ¡ ¤<br />
¨<br />
<br />
¡ <br />
<br />
¤ ¡ ¡ <br />
<br />
¤¤<br />
<br />
Gexnestdec<br />
<br />
¦ <br />
<br />
¢ ¤¥ ¦<br />
¤ <br />
¢<br />
<br />
<br />
<br />
§©¨§<br />
¥<br />
<br />
<br />
¢ ¤ ¨<br />
¨<br />
<br />
¦<br />
¤¥<br />
¨ <br />
¢ ¤ ¢<br />
Gexnolexicord<br />
¦ <br />
¤<br />
<br />
<br />
<br />
¡¡¡ ¡ <br />
<br />
¡ ¤¥<br />
<br />
¤ ¢<br />
<br />
§©¨§<br />
¤ <br />
¦<br />
<br />
<br />
<br />
¤ ¡ <br />
<br />
¤ ¡ <br />
<br />
¤ ¡ <br />
<br />
¤ ¡ <br />
¤ ¡ <br />
<br />
¤¤<br />
¨ ¢<br />
¤¤<br />
<br />
¢ ¨<br />
¦<br />
Gexpermute<br />
¦ <br />
<br />
¢¡ ¤¥<br />
§¨<br />
<br />
¢ <br />
¤<br />
¨ <br />
¨ <br />
¦<br />
¤¤¥<br />
¨<br />
<br />
<br />
<br />
¡¢ ¤<br />
¨ <br />
<br />
¤ <br />
<br />
¦<br />
<br />
<br />
<br />
¡ ¤¥ <br />
¤<br />
¨ <br />
¨<br />
¨<br />
<br />
<br />
<br />
<br />
<br />
¤¤¥<br />
¨<br />
<br />
<br />
¨<br />
<br />
¦<br />
Gexpermute2<br />
<br />
¦ <br />
<br />
¢¡ ¤¥<br />
§¨<br />
<br />
¢ <br />
¤<br />
¤<br />
<br />
¤ ¡ <br />
¤ ¡ <br />
27<br />
¤ ¡ <br />
¤ ¡ <br />
¤ ¡
¨<br />
¨ <br />
<br />
<br />
<br />
<br />
¤¤¥ <br />
¡¢ ¤<br />
¨<br />
<br />
<br />
¤ ¨<br />
<br />
¦<br />
<br />
<br />
<br />
¡ ¤¥ <br />
¤<br />
¨ <br />
¨<br />
¨<br />
<br />
<br />
<br />
<br />
<br />
¤¤¥<br />
¨<br />
<br />
<br />
¨<br />
<br />
¦<br />
<br />
Gexspl<br />
<br />
<br />
<br />
<br />
<br />
<br />
¤¥ <br />
<br />
¢¡£ ¤<br />
§©¨<br />
¢ ¤ ¢¡£<br />
¨<br />
¦<br />
¤¥ <br />
<br />
¢¡£ ¤ ¢<br />
¤<br />
¢¡ ¤<br />
<br />
¢¡£ ¤ ¨ <br />
¤ ¢¡ ¨<br />
¦<br />
<br />
¤¥ ¢¡£ ¤<br />
¨<br />
¢ ¤ ¢¡£<br />
¨<br />
¦<br />
¢¡ ¤<br />
¤¥ ¨ ¢ ¦ <br />
¢¡£<br />
Gextrick<br />
<br />
<br />
<br />
¤¥ ¢¡£<br />
¤<br />
§©¨§ <br />
¥ <br />
¤<br />
¨<br />
<br />
¢ ¢<br />
¢ ¤<br />
¨<br />
¤<br />
¨ <br />
¢ ¤ <br />
¨ § <br />
¡ ¤<br />
¨<br />
<br />
¦<br />
¤¥ ¢¡£<br />
¤ <br />
¨§<br />
<br />
¤ ¢<br />
¨<br />
<br />
¢¡ <br />
<br />
¤¤<br />
<br />
<br />
<br />
¢ ¤ ¡ § ¤<br />
¨<br />
¦ <br />
<br />
28
B Syntaks og semantik for et funktionelt programmeringssprog<br />
Syntaks<br />
¢¢ ¤ ¤<br />
<br />
<br />
¢¢ ¤<br />
<br />
<br />
<br />
¤<br />
¢¢<br />
<br />
<br />
¤ <br />
<br />
<br />
<br />
¢¤<br />
<br />
<br />
<br />
¦ ¢¢ ©<br />
¤ ¢¢<br />
<br />
¢¢<br />
Semantik<br />
Domains<br />
Types<br />
Semantic operator<br />
Auxiliary operations<br />
<br />
Assumption<br />
<br />
identifier not in Parameter<br />
primitive operator<br />
<br />
(a flat domain).<br />
<br />
<br />
, where<br />
for all<br />
.<br />
¢¦¦ <br />
<br />
¢¦¦ <br />
<br />
¢ ¦<br />
<br />
¦ ¢<br />
<br />
¦ ¦<br />
<br />
<br />
¤ £<br />
<br />
<br />
¦ <br />
(the natural injection)<br />
¦ <br />
£¤<br />
<br />
<br />
<br />
<br />
<br />
<br />
¤<br />
<br />
<br />
¦ <br />
<br />
True is a distinguished element of Value.<br />
<br />
<br />
<br />
<br />
if<br />
or ,<br />
if ,<br />
otherwise.<br />
for <br />
<br />
<br />
<br />
if<br />
<br />
<br />
<br />
and <br />
for each else<br />
<br />
<br />
<br />
least index such that <br />
29<br />
where