25.09.2013 Views

Size-change grafer

Size-change grafer

Size-change grafer

SHOW MORE
SHOW LESS

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

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!