4 Programkontruktion När man kriver tora program måte man, preci om man gör vid löning av tora problem, dela upp det hela i mindre delar. Hur ka man göra uppdelningen av ina tora program? Här måte man utgå ifrån vad ett program egentligen är, nämligen en plan för bearbetning av data eller information. Deutom ka man ha målättningen att programmen ka bli: korrekta robuta lätta att använda lätta att underhålla lätta att förändra återanvändbara Man har använt och använder fortfarande olika metoder för att kontruera tora program. Vilken metod om använd beror på vad det är för typ av program man gör och vilka målättningar man prioriterar. Här kommer metoderna för uppdelning med hjälp av tegvi förfining, dataflöden och abtrakta datatyper att genomgå. Metoderna poängterar antingen bearbetning (tegvi förfining), data (dataflöden) eller både data och bearbetning (abtrakta datatyper) i mer eller mindre grad och alla metoder har ina för och nackdelar. Ex: Ett mätytem ka hämta data i form av mätpoter till en vektor, ortera vektorn, läa in en nyckel och öka i vektorn efter denna nyckelpot amt avlutningvi beräkna och kriva ut vi data om denna pot. a) Stegvi förfining: Vad ka programmet göra? Vilka bearbetningar är aktuella? Finfördela vid behov. Mätprog Lä Bearbeta Skriv Sortera Sök Beräkna Fördelar : Logik uppdelning i mindre delar om automatikt blir funktioner. Nackdelar : Glömmer bort data. Alla datatyper måte definiera globalt och variabler av dea tranportera om parametrar och finn då i väldigt många funktioner vilket förvårar underhåll och förändringar. Man får deutom en dålig avbild eller modell av verkligheten, vilket ockå förvårar underhåll och förändringar. 61
b) Dataflöden: Hur flödar data? Hur bearbeta data? Funktion för varje bearbetning. Lä v v Sortera v Sök m Beräkna x Skriv Lä n n Häng upp funktionerna där inmatad data övergår till att bli utmatad, vid beräkna. Beräkna Sök Skriv Sortera Lä n Lä v Fördelar : Bättre avbild av verkligheten där även data beakta. Nackdelar : Fortfarande globala datatyper om förvårar underhåll och förändringar. c) Abtrakta datatyper: Vad ka programmet hantera? Vilka abtrakta datatyper, med data och funktioner är aktuella? Mätprog Mätvek datatyp lä ortera ök Mätpot datatyp lä jämför beräkna Fördelar: Data och operationer amlade på amma tälle. Lätt att underhålla och förändra. Deutom finn oftat variablerna eller objekten av de abtrakta datatyperna i verkligheten ockå. Bättre möjligheter till återanvändning. Nackdelar: Ingen logik tegvi förfining. Man måte börja tänka om i form av abtrakta datatyper. 62
4.1 Stegvi förfining Man utgår ifrån vad programmet ka göra eller med andra ord de operationer (initiera, läa, beräkna, mäta, kriva etc) om ingår i programpecifikationen. Varje operation blir då i princip en funktion i programmet på högta nivån. Därefter fortätter man uppdelningen genom att tegvi förfina operationerna till mindre operationer på lägre och lägre nivå. Fördelen med tegvi förfining är att man löer problem genom att kjuta onödiga detaljer framför ig och löa dea efterhand om de dyker upp. Man behöver ej ha hela problemtällningen i huvudet amtidigt. Nackdelen är den relativt fria kompoitionen. Samma program kan kriva på väldigt många olika ätt och programmet är ofta en dålig avbild av verkligheten. Vid underhåll och förändringar blir det vårt att hitta alla operationer om man ka ändra i, efterom data och operationer är åtkilda. Ex: Skriv ett program, exchange, om har hand om växling av valuta i ett bankytem. Programmet ka läa in valutanamn och växelbelopp och beräkna och kriva ut kotnaden för valutan i venka kronor. För beräkning av kotnad ka aktuell kur läa från en textfil kur.txt om er ut om: pund 14.32 udol 7.65 euro 9.12 Med tegvi förfining uppdelar vi programmet i mindre delar enligt : bankytem exchange lä valutanamn lä belopp beräkna kotnad kriv kotnad lä kur När man implementerar program, om är kontruerade med tegvi förfining, går man nätan alltid uppifrån och ner (top down). Man börjar med att implementera huvudprogrammet med globala datatyper och funktionprototyper. Därefter kan man gå till rep funktion och implementera dea en och en, amtidigt om man tetar programmet med en funktion åt gången. 63
#include <tdio.h> #include <tring.h> #include <math.h> enum valutanamn pund, udol, euro; enum valutanamn la_valutanamn(void) char tr[20]; enum valutanamn v; int fel; do fel = 0; printf("ge valutan namn : "); get(tr); if (trcmp(tr, "pund") == 0) v = pund; ele if (trcmp(tr, "udol") == 0) v = udol; ele if (trcmp(tr, "euro") == 0) v = euro; ele printf("felaktigt namn!\n"); fel = 1; while (fel); return v; double la_belopp(void) double b; char tr[80]; do printf("ge köpbelopp : "); get(tr); b = atof(tr); if (b <= 0) put("fel belopp!"); while (b <= 0); return b; 64
double la_kur(enum valutanamn v) FILE *vfil; char tr[20]; enum valutanamn v; double kur; vfil = fopen("kur.txt","r"); do fcanf(vfil, "%%lf", tr, &kur); if (trcmp(tr, "pund") == 0) v = pund; ele if (trcmp(tr, "udol") == 0) v = udol; ele if (trcmp(tr, "euro") == 0) v = euro; while ( v!= v); fcloe(vfil); return kur; double berakna_kotnad(enum valutanamn v, double b) return b * la_kur(v); void kriv_kotnad(enum valutanamn v, double b, double k) char tr[20]; witch (v) cae pund : trcpy(tr, "pund"); break; cae udol : trcpy(tr, "udol"); break; cae euro : trcpy(tr, "euro"); printf("%.2f % kotar %.2f kr", b, tr, k); int main() enum valutanamn val; double belopp, kotnad; val = la_valutanamn(); belopp = la_belopp(); kotnad = berakna_kotnad(val, belopp); kriv_kotnad(val, belopp, kotnad); return 0; 65
Ex: A Video Game ( ur Software Engineering, A Programming Approach) In order to illutrate the ue of functional decompoition we will deign the oftware for a imple video game, called breakout. Thi account can be kipped by the reader who feel they have a grap of the method. The game make ue of a keyboard and a crude viual diplay that can diplay character (but no other graphic) at any poition on the creen. The creen look like : wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwww o bbb The figure portray a wall, built from w character, a bat bbb and a ball the letter o. The ball bounce around the creen, moving up, down or diagonally, changing direction conventionally when it hit the wall, the ide or the bat. The player can move the bat left or right by keying l or r on the keyboard. The objective i to make the ball knock down the wall and o break out. The ball i out of play and vanihe when it pae the bat. The player i upplied with a ucceion of 4 ball. When thee have been ued the bet core ever attained i diplayed. Let u refer to place on the creen by a pair of x, y coordinate and aume that the oftware can aign a character value to a poition on the creen by executing a tatement like: creen(17, 22) = b Similarly uppoe that the value at any poition can be teted uing a tatement like: if creen(17, 22) == w We will refer to the coordinate of the ball a xball and yball. We begin with the overall logic: video game draw ide 66
draw bat at home poition while omeone want to play draw wall play a game diplay bet core We have relegated detail to tatement like draw ide and we can tudy and check the deign without worrying about detail. We probably realize that eential tep are miing o we might amend the above to: video game draw ide draw bat at home poition bet core = 0 while omone want to play core = 0 draw wall play a game if core > bet core bet core = core diplay bet core Now that we are atified with thi, we can chooe one of the tatement and write down what it doe in more detail. An obviou candidate i : play a game ball left = 4 while ball left > 0 play a ball ubtract one from ball left and then: play a ball create a new ball while ball i in play check ball poition move ball move bat erae ball Let u now work on detail of moving the bat: move bat 67
if key == l move left if key == r move right which require: move left erae old bat if bat poition > left wall + 1 bat poition = bat poition 1 draw bat Thi complete a fair amount of the detail concerned with moving the bat. We can leave futher detail for the time being and turn to conider more of the detail of moving the ball. move ball erae ball xball = xball + xtep yball = yball + ytep creen(xball, yball) = o but we have to be careful to check whether it ha hit omething: check ball poition check out of play check ide check bat check brick We will finih the account of thi development with jut two more piece: check ide if ball at either ide xtep = xtep if ball at end boundary ytep = ytep check brick if creen(xball + xtep, yball + ytep) i a brick creen(xball + xtep, yball + ytep) = add one to core ytep = ytep ubtract one from brick left if brick left == 0 draw wall There i till ome way to go with thi particular deign, but thi much give the flavor of the deign and of the method. Notice how, throughout, we can check the deign 68
without having to take account of a lot of unneceary detail. Notice the informality of the tatement ued and the control tructure if and while. 69
4.2 Dataflöden Man utgår ifrån hur data flödar i programmet, från indata fram till utdata. Man koncentrerar ig på data och hur data ka bearbeta av programmet enligt programpecifikationen. Varje bearbetning blir då i princip en funktion i programmet. Därefter tar man den funktion, där inmatad data övergår till att bli utmatad, till huvudfunktion och låter reten av funktionerna anropa utgående ifrån dataflödena, om kan likna vid nören om binder ihop funktionerna. Fördelen med att använda dataflöden är att man bygger itt program på hur verkligheten er ut. Man får en väl förankrad modell av verkligheten om är bra att ha då programmet ka underhålla och förändra. Deutom får man en naturlig uppdelning av programmet i mindre delar. Nackdelen är dock att data och operationer på denna data fortfarande är åtkilda och det är vårt att hitta alla förändringar om ka göra då exempelvi data ka förändra. Ex: Skriv ett program exchange om har hand om växling av valuta i ett bankytem. Programmet ka läa in valutanamn och växelbelopp och beräkna och kriva ut kotnaden för valutan i venka kronor. För beräkning av kotnad ka aktuell kur läa från en textfil kur.txt om er ut om: pund 14.32 udol 7.65 euro 9.12 Genom att betrakta dataflödet får man en naturlig uppdelning enligt : Lä kur kur, valutanamn Lä valutanamn Lä belopp valutanamn belopp Beräkna kotnad Kotnad, belopp, Skriv kotnad valutanamn Beräkna kotnad Lä kur Lä belopp Skriv kotnad Lä valutanamn 70
#include <tdio.h> #include <tring.h> #include <math.h> enum valutanamn pund, udol, euro; double la_belopp(void) double b; char tr[80]; do printf("ge köpbelopp : "); get(tr); b = atof(tr); if (b <= 0) put("fel belopp!"); while (b <= 0); return b; enum valutanamn la_valutanamn(void) char tr[20]; enum valutanamn v; int fel; do fel = 0; printf("ge valutan namn : "); get(tr); if (trcmp(tr, "pund") == 0) v = pund; ele if (trcmp(tr, "udol") == 0) v = udol; ele if (trcmp(tr, "euro") == 0) v = euro; ele printf("felaktigt namn!\n"); fel = 1; while (fel); return v; 71
double la_kur(enum valutanamn *vp) FILE *vfil; char tr[20]; enum valutanamn v; double kur; v = la_valutanamn(); vfil = fopen("kur.txt","r"); do fcanf(vfil, "%%lf", tr, &kur); if (trcmp(tr, "pund") == 0) *vp = pund; ele if (trcmp(tr, "udol") == 0) *vp = udol; ele if (trcmp(tr, "euro") == 0) *vp = euro; while ( *vp!= v); fcloe(vfil); return kur; void kriv_kotnad(enum valutanamn v, double b, double k) char tr[20]; witch (v) cae pund : trcpy(tr, "pund"); break; cae udol : trcpy(tr, "udol"); break; cae euro : trcpy(tr, "euro"); printf("%.2f % kotar %.2f kr", b, tr, k); int main() enum valutanamn v; double belopp, kotnad; belopp = la_belopp(); kotnad = belopp * la_kur(&v); kriv_kotnad(v, belopp, kotnad); return 0; 72
Ex: Monitoring A Plant ( ur Software Engineering, A Programming Approach) Here i the pecification for a oftware ytem: A computer i being ued to monitor an indutrial plant. The computer periodically input reading from intrument in the plant. Some of the reading require converion to normal unit of meaurement (e.g microvolt into degree). The computer check each of the reading againt permiible value. Alarm report are diplayed on a creen when a value i outide it valid range. The data flow diagram for thi problem i : Get time time Determine intrument Meaure intrument Get conv. factor converion factor Convert data converted data Get limit limit Check meage Diplay meage It i relatively clear that the central tranform in the ytem i the bubble that check the value. Hence we can derive the program tructure diagram a: Check Get limit Convert Diplay meage Get conv. factor Meaure Determine intrument Get time 73
4.3 Abtrakta datatyper Vad ka programmet hantera? Vilka abtrakta datatyper i form av data och operationer på denna typ av data är aktuella? Till killnad mot föregående metoder koncentrerar man ig här på både data och operationer och föröker amla dea på ett och amma tälle. När man har kontruerat de aktuella abtrakta datatyperna, om använd i programmet, kan en användare, om kan vara en annan abtrakt datatyp eller ett huvudprogram, kapa variabler eller objekt av dea. Fördelen med att använda abtrakta datatyper är att man amlar data och operationer på denna typ av data, på amma tälle, i amma modul och därför blir det mycket lättare att underhålla och förändra programmen. Deutom blir det naturligt att återanvända de abtrakta datatyperna i andra program. Nackdelen är att man måte tänka om vid programkontruktionen. Man kan inte följa den mer logika tegvia förfiningen utan man börjar itället med att leta efter abtrakta datatyper alltå datatyper utöver de vanliga double, char, int etc om programmet ka hantera och om man ofta avbildar om egendefinierade poter. Efter att ha hittat lämpliga abtrakta datatyper letar man efter till dea hörande operationer. Sedan underöker man hur de olika abtrakta datatyperna ka amarbeta för att hela programmet ka fungera och lutligen implementerar man de olika delarna. Ex: Skriv ett program exchange om har hand om växling av valuta i ett bankytem. Programmet ka läa in valutanamn och växelbelopp och beräkna och kriva ut kotnaden för valutan i venka kronor. För beräkning av kotnad ka aktuell kur läa från en textfil kur.txt om er ut om: pund 14.32 udol 7.65 euro 9.12 Vad ka programmet hantera? Valutor! Här finn den abtrakta datatypen valuta om man kan avbilda om en pot innehållande termerna valutanamn och kur. De operationer om är aktuella är läa, kriva och beräkna kotnad för valuta. Efterom man bara har en abtrakt datatyp kommer det bara att vara huvudprogrammet om använder denna och beroendediagrammet blir enligt: Exchange Valuta 74
För att implementera den abtrakta datatypen valuta använder man ig i C av en pecifikationfil valuta.h och en implementationfil valuta.c enligt: /* Specifikation av valuta valuta.h */ enum valutanamn pund, udol, euro; typedef truct enum valutanamn namn; double kur; valuta; void la_valuta(valuta *vp); void kriv_valuta(valuta v); double berakna_kotnad(valuta v, double belopp); /* Implementation av valuta valuta.c */ #include "valuta.h" #include <tdio.h> #include <tring.h> enum valutanamn la_valutanamn(void) char tr[20]; enum valutanamn v; int fel; do fel = 0; printf("ge valutan namn : "); get(tr); if (trcmp(tr, "pund") == 0) v = pund; ele if (trcmp(tr, "udol") == 0) v = udol; ele if (trcmp(tr, "euro") == 0) v = euro; ele printf("felaktigt namn!\n"); fel = 1; 75
while (fel); return v; 76
double la_kur(enum valutanamn v) FILE *vfil; char tr[20]; enum valutanamn v; double kur; vfil = fopen("kur.txt","r"); do fcanf(vfil, "%%lf", tr, &kur); if (trcmp(tr, "pund") == 0) v = pund; ele if (trcmp(tr, "udol") == 0) v = udol; ele if (trcmp(tr, "euro") == 0) v = euro; while ( v!= v); fcloe(vfil); return kur; void la_valuta(valuta *vp) vp >namn = la_valutanamn(); vp >kur = la_kur(vp >namn); double berakna_kotnad(valuta v, double belopp) return ( belopp * v.kur); void kriv_valuta(valuta v) char tr[20]; witch (v.namn) cae pund : trcpy(tr, "pund"); break; cae udol : trcpy(tr, "udol"); break; cae euro : trcpy(tr, "euro"); printf("\nvalutanamn : %\n", tr); printf("kur : %.2f\n", v.kur); OBS! Vid implementationen utnyttjar man egna lokala funktioner om ej exportera och alltå ej finn med i headerfilen. 77
Huvudprogrammet kan nu utnyttja den abtrakta datatypen genom att kapa variabler eller objekt av denna datatyp och utnyttja de funktioner om exportera för den abtrakta datatypen. /* Huvudprogram exchange.c */ #include <tdio.h> #include <math.h> #include "valuta.h" double la_belopp(void) double b; char tr[80]; do printf("ge köpbelopp : "); get(tr); b = atof(tr); if (b <= 0) put("fel belopp!"); while (b <= 0); return b; int main() valuta v; double b; la_valuta(&v); b = la_belopp(); kriv_valuta(v); printf("belopp: %.2f\n", b); printf("kotnad: %.2f\n", berakna_kotnad(v, b)); return 0; OBS!Hur man även här använder andra funktioner utöver de från den abtrakta datatypen. 78
En av fördelarna med att använda abtrakta datatyper är att dea ka kunna återanvända av andra program. I utvecklingmiljön för C program brukar det finna färdiga abtrakta datatyper om: tdio.h in och utmatning, ingår i C tandarden do.h dokommandon i Window conio.h videofunktioner för Window i textmode Har man ej tillgång till färdiga återanvändbara abtrakta datatyper måte man kriva dea jälva. Exempel på ådana är lithantering i lifo.h, fifo.h och twolit.h om genomgått tidigare och om viar hur man kan återanvända abtrakta datatyper. Problemet vid återanvändning av exempelvi twolit.h är att den, i den form om bekrivit ovan, enbart kan använda för en enda typ av data. Vill man använda flera olika tvåväglitor i itt program kan man använda poter innehållande unioner om data. Ex: Skriv ett program om läer in fordon med regitreringnummer, ägare och typ om kan vara peronbil, latbil med tillåten latvikt i ton eller bu med tillåtet antal paagerare från en textfil enligt: EEE555 E.E peron AAA111 A.A bu 10 CCC333 C.C bu 30 GGG777 G.G lat 7.7 BBB222 B.B peron FFF666 F.F lat 6.6 DDD444 D.D bu 40 och toppar in dea i en tvåväglita orterad efter regitreringnummer. Programmet ka fortätta med att kapa en ny tvåväglita innehållande bara buar och edan kriva ut hela fordonlitan och bulitan på kärmen enligt: Fordonlita AAA111 A.A bu 10 BBB222 B.B peron CCC333 C.C bu 30 DDD444 D.D bu 40 EEE555 E.E peron FFF666 F.F lat 6.600000 GGG777 G.G lat 7.700000 Bulita AAA111 A.A bu 10 CCC333 C.C bu 30 DDD444 D.D bu 40 79
Man börjar med att identifiera vilka objekt om programmet ka hantera och därmed vilka abtrakta datatyper om ka kapa eller återanvända. I pecifikationen tala om fordon, fordonlitor, buar och bulitor. Efterom det finn en färdig abtrakt datatyp för tvåväglitor, om man kan använda för en enda aktuell datatyp i itt program, måte man föröka avbilda alla typer av fordon med en enda datatyp. Därmed räcker det med den abtrakta datatypen fordon för alla typer av fordon. Fordon kommer att avbilda enligt: enum fordontyp peron, lat, bu; typedef truct fordonpot char regnr[10]; char agare[30]; enum fordontyp ftyp; union double vikt; int pa; u; fordon; Med ovantående union ingående i poten markera att innehållet i minnet kan vara antingen en double i form av vikten eller ett heltal om håller reda på antalet tillåtna paagerare. Man måte jälv utgående ifrån ftyp hålla reda på vad om finn i u. För termen u allokera alltid minne för det törta möjliga innehållet. Nu kan man rita en beroendegraf för de abtrakta datatyper om programmet kommer att innehålla. fordmain fordon fordlit twolit 80
/* Specifikation av fordon fordon.h */ #ifndef FORDONH #define FORDONH #include <tdio.h> enum fordontyp peron, lat, bu; typedef truct fordonpot char regnr[10]; char agare[30]; enum fordontyp ftyp; union double vikt; int pa; u; fordon; void la_fordon(file *infil, fordon *fp); void kriv_fordon(fordon f); int i_le_fordon(fordon f1, fordon f2); int i_a_bu(fordon f); #endif /* Implementation av fordon fordon.c */ #include "fordon.h" #include <tdio.h> #include <tring.h> void la_fordon(file *infil, fordon *fp) char tr[10]; double x; fcanf(infil, "%%%", fp >regnr, fp >agare, tr); if ( trcmp(tr, "peron") == 0 ) fp >ftyp = peron; ele if ( trcmp(tr, "lat") == 0 ) fp >ftyp = lat; fcanf(infil, "%lf", &x); fp >u.vikt = x; ele if ( trcmp(tr, "bu") == 0 ) fp >ftyp = bu; fcanf(infil, "%d", &fp >u.pa); 81
void kriv_fordon(fordon f) char tr[10]; printf("% 10% 20", f.regnr, f.agare); witch (f.ftyp) cae peron : printf("peron\n");break; cae lat : printf("lat %f\n", f.u.vikt); break; cae bu : printf("bu %d\n", f.u.pa); int i_le_fordon(fordon f1, fordon f2) return (trcmp(f1.regnr, f2.regnr) < 0 ); int i_a_bu(fordon f) return (f.ftyp == bu); /* Specifikation av tvåväglitor twolit.h */.. #include fordon.h typedef fordon datatyp;.. /* Specifikation av fordonlitor fordlit.h */ #ifndef FORDLISTH #define FORDLISTH #include "fordon.h" #include "twolit.h" void fyll_fordlit(headtyp *fp); void via_fordlit(headtyp *fp); void fyll_bulit(headtyp *fp, headtyp *bp); #endif 82
/* Implementation av fordonlitor fordlit.c */ #include <tdio.h> #include "fordlit.h" void fyll_fordlit(headtyp *hfp) FILE *fil; fordon f; linktyp *lfp; fil = fopen("fordon.txt", "r"); la_fordon(fil, &f); while (!feof(fil) ) newlink(&lfp); putlink(f, lfp); inort(lfp, hfp, i_le_fordon); la_fordon(fil, &f); fcloe(fil); void via_fordlit(headtyp *hfp) fordon f; linktyp *lfp; lfp = firtlink(hfp); while ( lfp!= NULL ) f = getlink(lfp); kriv_fordon(f); lfp = ucclink(lfp); void fyll_bulit(headtyp *hfp, headtyp *hbp) fordon f; linktyp *lfp, *lbp; lfp = firtlink(hfp); while ( lfp!= NULL ) f = getlink(lfp); if ( i_a_bu(f) ) newlink(&lbp); putlink(f, lbp); inlat(lbp, hbp); lfp = ucclink(lfp); /* Huvudprogram fordmain.c */ 83
#include "twolit.h" #include "fordlit.h" int main() headtyp *fordhp, *buhp; newhead(&fordhp); newhead(&buhp); fyll_fordlit(fordhp); put("fordonlita"); via_fordlit(fordhp); fyll_bulit(fordhp, buhp); put("\nbulita"); via_fordlit(buhp); return 0; Ovan använde man union del i poten för att hantera litor med varierande data i itt program. Det finn ett alternativt ätt att använda den abtrakta datatypen twolit för att hantera flera litor med olika typer av data i. Man kan om datatyp ange en void pekare enligt: /* Specifikation av tvåväglitor twolit.h */.. typedef void * datatyp;.. På detta ätt får man en lita innehållande data i form av pekare till aktuella data. Denna lita blir mer generell och kan återanvända i färdig kompilerad form. Detta är en fördel när man äljer program. Man behöver bara leverera den makinkodade exekveringfilen. Nackdelen med att använda pekare om data i litor är att man ej får någon kompileringäkerhet. Hela anvaret för att korrekt data finn i litan överlämna åt programmeraren. 84