• No results found

Applikationens funktionalitet och systemkrav

6.5 Sammanfattning av huvudsyftet med applikationerna

7.3.5 Applikationens funktionalitet och systemkrav

Alla versionerna av Clock uppfyller kravspecifikationen och beter sig på det sätt som man förväntar sig av en Windowsapplikation. De skillnader som finns i funktionaliteten har redan nämnts på sidorna 51 och 68. Visual C++-versionen har fått sin extra funktionalitet tack vare att den fås automatiskt via det färdiga ramverket. För .NET-versionen handlar det om användbara utökningar som enkelt lät sig implementeras.

Om Visual C++-versionen kompileras med rätt inställning i Visual Studio blir resultatet en körbar exe-fil som inte är beroende av några externa filer för att kunna exekvera (utöver de som ingår i en någorlunda aktuell version av Windows). Applikationen ska fungera i alla Windowsversioner från Windows 95 och framåt (detta har dock inte testats). Det är också vanligt att avstå från att statiskt länka in MFC-biblioteket och istället låta exe-filen använda dess DLL-filer. Fördelen med detta är att programfilen blir mindre.

C#- och VB-versionerna blir på gott och ont beroende av .NET-ramverket. Resultatet vid kompileringen blir en exe-fil, men för att den ska kunna köras måste .NET-ramverket (version 1.1 eller senare) vara installerat på datorn. Det kräver minst Windows 98 (dvs det fungerar inte med Windows 95) för att kunna installeras. Visual Studio.NET har funktioner för att skapa ett installationsprojekt som förutom applikationen inkluderar installationsfilen för .NET-ramverket. Om applikationen inte levereras i fysisk form på CD-ROM utan via Internet kan det vara lämpligt att skapa två installationsversioner — en utan .NET-ramverket och en med — så att den som redan har ramverket installerat inte behöver ladda ner en onödigt stor fil.

Problem kan uppstå om användaren sitter vid en maskin som inte har ramverket instal- lerat och inte har användarbehörighet för att installera det. Storleken (ca 20 MB) är heller inte helt smärtfri vid en långsam Internet-uppkoppling. Det fungerar som sagt heller inte om operativsystemet är Windows 95. Med andra ord finns det vissa lägen då det kan vara svårt eller omöjligt att köra .NET-applikationer, vilket man bör ta hänsyn till vid valet av språk.

Kapitel 8

Applikationen WordCount

I detta avsnitt studeras de 20 versionerna av applikationen WordCount. Versionerna skiljer sig åt i flera avseenden men alla implementerar någon variant av följande övergripande algoritm:

1. Ta fram det ord som står på tur i indatafilen. 2. Gör om eventuella versaler till gemener. 3. Ta reda på om ordet har hittats tidigare.

4. (a) I så fall: öka antalet förekomster för detta ord med 1.

(b) Annars: lägg till ordet till de funna orden (med antalet förekomster satt till 1). 5. Upprepa punkterna 1–4 tills hela indatafilen gåtts igenom.

6. Sortera listan över funna ord i fallande ordning efter antalet förekomster. 7. Skriv ut de 20 första orden i den sorterade listan (eller så många som finns).

Samtliga versioner har det gemensamt att de tar indatafilen som ett kommandoradsargu- ment. För att underlätta testningen används en fil med en hårdkodad sökväg om inget argu- ment anges. Om applikationen skulle användas seriöst skulle bättre felhantering behövas. Beteendet för implementeringarna här blir odefinierat om indatafilen inte finns eller om något annat filhanterings- eller minnesrelaterat problem skulle uppstå.

I det följande beskrivs först versionerna en och en varefter de jämförs med varandra. Den fullständiga källkoden till varje version finns tillgänglig via [12]. Här kommer de viktigaste delarna att presenteras. Det som är gemensamt upprepas inte vid beskrivningen av varje enskild version.

8.1

C++

Eftersom C++ är ett språk som funnits länge (och inkluderar det ännu äldre C som en del- mängd) finns det en uppsjö av olika sätt — som alla kan betraktas som naturliga — att im- plementera en applikation som WordCount på. De 13 olika versioner som presenteras här är på intet vis uttömmande, utan ska ses som några representativa exempel på hur applika- tionen kan skrivas.

8.1.1 ”Simpel” (C++ 1)

Denna version tar sin utgångspunkt i att programmeraren inte har tillgång till några färdiga klasser (eller inte känner till att de finns) och måste (eller vill) skriva så mycket som möjligt av koden själv. Ett sätt att se det är att versionen är vad man skulle kunna tänka sig att en oinformerad hobbyprogrammerare skulle skrivit för säg 10–15 år sedan (innan Internet gjorde det enklare att vara välinformerad). Den motsvarar också nära vad författaren faktiskt gjorde vid denna tid, även om det skedde i språket AmigaBASIC.

Programmet skulle i princip lika gärna kunna vara ett C-program som ett C++-program. Det utnyttjar ingen objektorientering. De enda ”externa element” som används är funktio- nerna fopen, fseek, ftell, fread, fclose, strcmp och printf från C-standardbiblioteket. Av läsbarhetsskäl har författaren dock inte avstått från att utnyttja C++-finessen att variabler kan deklareras och initialiseras var som helst. Minnesallokering med new samt datatypen bool används också.

Algoritmen ovan implementeras på enklast möjliga sätt. Textfilen läses in i minnet och gås igenom tecken för tecken med kontroll av om ett separeringstecken hittats. De funna or- den lagras i en array av fast storlek som pekare till de platser i minnesblocket med textfilen där den första instansen hittats. För att kontrollera om ett nyfunnet ord redan har hittats jäm- förs det med vart och ett av de tidigare funna orden tills ordet hittas eller hela arrayen gåtts igenom. Till sorteringen används den mycket ineffektiva men enkla sorteringsalgoritmen bubble sort.1

Koden inleds före main med ett par #include-direktiv samt följande:

int nr_of_words = 0; int counts[16384]; char* words[16384]; void sort(); 1 2 3 4

C++

Variabeln nr_of_words och de båda arrayerna counts och words görs globala för att slippa skicka med dem som argument till sort-funktionen. Antalet olika ord som versionen kan hantera bestäms till 16384 stycken, i linje med kravet i specifikationen (se avsnitt 6.2 sid 48).

main inleds så här:

int main(int argc, char* argv[])

{

// Hårdkodad sökväg som används för indatafilen när inget

// kommandoradsargument anges

const char* filename = "C:\\Text\\Books\\milnd11.txt"; if (argc == 2)

{

// Använd indatafil från kommandoradsargument

filename = argv[1]; } 1 2 3 4 5 6 7 8 9 10 11

C++

1Se t exen.wikipedia.org/wiki/Bubble_sort

8.1. C++

// Öppna filen

FILE* fp = fopen(filename, "rb");

// Ta fram filens längd

fseek(fp, 0, SEEK_END);

int file_length = ftell(fp);

fseek(fp, 0, SEEK_SET);

// Allokera ett minnesblock av rätt storlek och läs in filen

char* buffer = new char[file_length + 1];

fread(buffer, 1, file_length, fp);

fclose(fp);

// Se till att bufferten slutar med ett separeringstecken

buffer[file_length++] = ' '; 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

C++

Om ingen fil anges som kommandoradsargument framgår det att en hårdkodad sökväg an- vänds. Eftersom bakåtsnedstreck används för escape-sekvenser måste alla bakåtsnedstreck i Windowssökvägar dubbleras. Filen öppnas med fopen, läses in i sin helhet i en buffert (char-array) med fread och stängs med fclose. Funktionerna fseek och ftell används för att få fram filens längd eftersom det saknas en färdig funktion för detta. Bufferten allokeras till ett tecken mer än fillängden och ett blankstegstecken placeras i slutet. På så sätt garanteras att bufferten slutar med ett separeringstecken vilket förenklar den efterföljande koden.

Följer gör den kod som undersöker bufferten tecken för tecken och på så sätt plockar ut orden:

int current_word_start = 0;

for (int i = 0; i < file_length; i++)

{

char ch = buffer[i];

// Kolla om tecknet inte är ett separeringstecken

if (!(ch == ' ' || ch == ',' || ch == '.' || ch == '!' || ch == '?' || ch == ';' || ch == '"' || ch == ':' || ch == '(' || ch == ')' || ch == '\n' || ch == '\r'))

{

// Gör om eventuell versal till gemen

buffer[i] = ch | 32; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14

C++

För varje tecken undersöks om det är ett separeringstecken. Om så inte är fallet räknas det som ett ordtecken. Enligt specifikationen (se avsnitt 6.2) ska alla bokstäver göras om till gemener. Detta utförs genom att sätta den sjätte biten, ekvivalent med bitvis OR med värdet 32 (= 25). Om man tittar på en ASCII-tabell2ser man att detta fungerar och att bara några få övriga tecken påverkas. Konverteringen utförs genom direkt ändring i bufferten.

Koden fortsätter inuti en else-sats med fallet att det funna tecknet är ett separeringstec- ken:

if (i - current_word_start > 0)

{

// Ändra separeringstecknet till en nolla för att markera strängslut

buffer[i] = 0;

char* current_word = &buffer[current_word_start];

// Kolla om ordet hittats tidigare (se texten)

bool found = false;

for (int j = 0; j < nr_of_words; j++)

{ if (strcmp(current_word, words[j]) == 0) { counts[j]++; found = true; break; } } if (!found) {

words[nr_of_words] = current_word; counts[nr_of_words] = 1; nr_of_words++; } } current_word_start = i + 1; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

C++

Först kontrolleras om ett ord verkligen har hittats. Detta test kommer bli falskt då flera separeringstecken förekommer i följd, och i så fall behöver inget göras förutom att sätta variabeln current_word_start till att peka på nästa tecken i bufferten. Om ett ord hittades skapas en nollavslutad sträng genom att separeringstecknet ersätts med en nolla i bufferten. En lokal variabel som pekar ut det nyfunna ordet deklareras.

Den del av koden som gör grovjobbet återfinns på raderna 8–23. Här kontrolleras om ordet redan hittats genom att gå igenom och jämföra med de tidigare funna orden. Finns ordet ökas antalet förekomster med 1, annars läggs det till sist i arrayen och nr_of_words räknas upp. Det är enkelt att konstatera att detta fungerar korrekt, samtidigt som man bör inse att det riskerar att bli ineffektivt. Exakt hur ineffektivt kommer framgå av prestanda- mätningarna.

Återstår gör sorteringen samt utskriften av de 20 vanligaste orden med tillhörande antal förekomster:

sort();

for (i = 0; i < (nr_of_words < 20 ? nr_of_words : 20); i++)

{

printf("%s %d\n", words[i], counts[i]);

} delete[] buffer; 1 2 3 4 5 6 7 8

C++

Implementationen av sort-funktionen visas inte här; den är en rättfram implementering av bubble sort. Vid utskriften av de vanligaste orden kontrolleras att indatafilen innehöll minst 20 olika ord. I annat fall begränsas utskriften till det faktiska antalet. Slutligen frigörs det

8.1. C++

minne som allokerades till bufferten. Eftersom orden hela tiden lagras där (words-arrayen innehåller enbart pekare) är det viktigt att detta inte görs innan utskriften.

Related documents