Δευτέρα 18 Μαΐου 2009

Στο προηγούμενο άρθρο είδαμε μια γενική περιγραφή της κατάστασης που επικρατεί στο χώρο της πληροφορικής από τα μάτια ενός προγραμματιστή, μηχανικού λογισμικού, software Architect, ή όπως θέλετε πείτε τον. Η ουσία είναι η ίδια,,, περίπου!

Θα συνεχίσουμε εδώ περιγράφοντας το τρόπο σκέψης, το λεγόμενο «κατακερματισμό» της σκέψης σε απλά, διαδοχικά βήματα έτσι ώστε η περιγραφή των κινήσεων να αποδώσει μια ακολουθία ικανή ώστε να περιγράψει μια ενέργεια. Η πληθώρα τους ακολουθεί το μοντέλο «διαίρει και βασίλευε». Τί σημαίνει αυτό? Ότι, απλούστατα, σπάζοντας ένα πρόβλημα σε πολλά μικρά προβληματάκια, και επιλύοντας τα ένα προς ένα, καταλήγεις, αναπόφευκτα, στην επίλυση του συνολικότερου προβλήματος. Και φυσικά εξηγούμαι:

Ας πάρουμε σαν παράδειγμα κάτι απλό: Την ανάγνωση, άθροιση και εμφάνιση του αποτελέσματος στην οθόνη, 5 αριθμών που βρίσκονται σε καθε γραμμή, από ένα αρχείο κειμένου!! Ας δούμε λοιπόν τα δεδομένα:

• Κατ’αρχήν ξέρουμε οτι είναι ένα αρχείο κειμένου και οτι θα το διαβάσουμε.
• Ότι διαβάζουμε ακριβώς 5 αριθμούς και τους αθροίζουμε.
• Ότι κάθε αριθμός βρίσκεται σε ξεχωριστή γραμμή μέσα στο αρχείο.

Ωραία μέχρι εδώ ... Ώρα όμως για μερικές αποφάσεις: Τί είδους γλώσσα να χρησιμοποιήσουμε? Τί μας προσφέρει η μία που δε μας προσφέρει η άλλη? Στη προκειμένη περίπτωση είναι απειροελάχιστες οι διαφορές. Τίποτα ουσιαστικό δεν διαφοροποιεί τη κατάσταση. Γι’αυτό θα χρησιμοποιήσω μια γλώσσα που ξεκίνησε αμειγώς ως μαθηματική και επεκτάθηκε ως ένα πανίσχυρο εργαλείο στη φαρέτρα του προγραμματιστή. Τη Delphi!

Ας υποθέσουμε λοιπόν οτι το αρχείο που πρέπει να διαβάσουμε λέγεται InputFile.txt και περιέχει τους εξής 5 αριθμούς:

12345
21211
11000
21980
31978

Ορίζουμε με βάση τα παραπάνω οτι χρειαζόμαστε 5 διαδικασιούλες για να σπάσουμε το πρόβλημα σε μικρότερα προβληματάκια. (οι 5 διαδικασίες δεν έχουν καμία σχέση με τα 5 νούμερα που επιλέξαμε να περιέχονται στο αρχείο που θα διαβάσουμε). Η διαδικασίες λοιπόν έχουν ως εξής:

1. Θέλουμε μια διαδικασία για να ανοίξουμε το αρχείο
2. Μια διαδικασία που διαβάζει γραμμές από το αρχείο.
3. Μια διαδικασία που αθροίζει το διαβασμένο νούμερο από το αρχείο.
4. Μία διαδικασία που να δείχνει το αποτέλεσμα.
5. και τέλος μια διαδικασία που κλείνει το αρχείο.

Αυτές ορίζονται παρακάτω ως εξής:

Procedure _OpenFile;
Ανοίγει το αρχείο που ορίσαμε οτι ονομάζεται «InputFile.txt»

function ReadLine( LineNumber: Integer ): String;
Διαβάζει μια συγκεκριμένη γραμμή του αρχείου που ορίζεται στη παράμετρο LineNumber και την επιστρέφει.

function SumVal ( aCurrentValue: Integer; aLineNumber: Integer ): Integer;
Αθροίζει τη τιμή που περιέχεται στην aCurrentValue με την τιμή που περιέχεται στη γραμμή του αρχείου aLineNumber και επιστρέφει το άθροισμα.

procedure ShowResult ( aResult : Integer );
Δείχνει το αποτέλεσμα που περιέχεται στην παράμετρο aResult στην οθόνη.

Procedure _CloseFile;
Κλείνει το αρχείο InputFile.txt

Ο πλήρης κώδικας του προγράμματος παρεθέτεται παρακάτω. Λειτουργεί κανονικά! Με βάση τα όσα είπαμε το αποτέλεσμα του προγράμματος είναι αυτό:

"File Summary Result : 98514"

Θα προσπαθήσω να σχολιάσω το κώδικα που σας δίνω παρακάτω όσο καλύτερα μπορώ ώστε να καταλάβετε τι γίνεται. Τα σχόλια βρίσκονται στο τέλος κάθε γραμμής και ξεκινάνε με «//».

program Programming; // Όνομα προγράμματος

{$APPTYPE CONSOLE}

uses Windows, System, SysUtils, Classes; // Βιβλιοθήκες συστήματος που χρειαζόμαστε

Type
TFileReadAndSum = Class(TComponent) // Δήλωση αντικειμένου
private
LocalFile : TextFile; // Χειριστής αρχείου κειμένου
public
Constructor Create(aOwner: TComponent); override; // Δημιουργία Αντικειμένου
Destructor Destroy; override; // Καταστροφέας Αντικειμένου

Procedure _OpenFile; // Διαδικασία ανοίγματος αρχείου
function ReadLine( LineNumber: Integer ): String; // Διαδικασία ανάγνωσης γραμμής
function SumVal ( aCurrentValue: Integer; aLineNumber: Integer ): Integer; // Αθροιστής
procedure ShowResult ( aResult : Integer ); // Παρουσίαση αποτελέσματος στην οθόνη
Procedure _CloseFile; // Κλείσιμο αρχείου
End;

Το κομμάτι που μόλις γράψαμε αποτελεί τη «δήλωση» των διαδικασιών που θα χρησιμοποιήσουμε. Δηλαδή την ταυτότητα των υπορουτίνων του προγράμματος. Υπορουτίνα ορίζουμε μια εσωτερική, αυτόνομη διαδικασία που εκτελεί συγκεκριμένες λειτουργίας και επιστρέφει συγκεκριμένα αποτελέσματα, ανάλογα προς τις τιμές των παραμέτρων που εισάγονται σε αυτή. Έτσι, για παράδειγμα η υπορουτίνα SumVal δέχεται 2 παραμέτρους: την aCurrentValue που πρέπει να είναι αριθμητική και την aLineNumber που επίσης πρέπει να είναι αριθμητική. Επίσης επιστρέφει μια επίσης αριθμητική τιμή. Ωραία όλα αυτά αλλά δεν έχουμε τελειώσει.. ίσα ίσα μόλις ξεκινήσαμε. Πρέπει να γράψουμε λοιπόν τι θα κάνει κάθε μια από αυτές τις υπορουτίνες. Οπότε προχωράμε στην υλοποίηση (το γράψιμο δηλαδή) του κώδικα που τις απαρτίζουν:


{ TFileReadAndSum }
// Δημιουργούμε ένα στιγμιότυπο/αντίγραφο του αντικειμένου TFileReadAndSum:
constructor TFileReadAndSum.Create(aOwner: TComponent);
begin
inherited Create(AOwner);
end;

// Καταστρέφουμε ένα στιγμιότυπο/αντίγραφο του αντικειμένου TFileReadAndSum:
destructor TFileReadAndSum.Destroy;
begin
inherited Destroy;
end;

// Κλείνουμε το αρχείο, κλείνοντας το χειριστή του αρχείου InputFile.txt που ορίσαμε ως LocalFile.
procedure TFileReadAndSum._CloseFile;
begin
CloseFile(LocalFile);
end;

// Ανοίγουμε το αρχείο, ανοίγοντας το χειριστή του αρχείου InputFile.txt που ορίσαμε ως LocalFile.
procedure TFileReadAndSum._OpenFile;
begin
AssignFile( LocalFile, 'InputFile.txt');
Reset(LocalFile);
end;

// Διάβασμα μιας γραμμή από το αρχείο που ο αριθμός της ( 1 – 5) ορίζεται από την παράμετρο LineNumber
function TFileReadAndSum.ReadLine(LineNumber: Integer): String;
var
Counter: Integer; // Δήλωση μεταβλητής μέτρησης
begin
if LineNumber <= 5 then // Αν η γραμμή που περιμένουμε >5 έχουμε πρόβλημα!
begin
Reset(LocalFile); // Ξεκινάμε από την αρχή του αρχείου ...
For Counter := 1 to 5 do // ... και για κάθε γραμμή από 1 έως την 5 ...
begin
ReadLn(LocalFile, Result); // ... διαβάζουμε τη γραμμή ...
if Counter = LineNumber then Exit; // ... και αν ο μετρητής 1 έως 5 είναι = με τη γραμμή που
end; // περιμένουμε τότε επιστρέφουμε τη τιμή και φεύγουμε.
end
else
Result := ''; // Αν LineNumber >5 τότε δεν επιστρέφουμε τίποτα.
end;

// Εμφάνιση στην οθόνη του αποτελέσματος που περιέχεται στην παράμετρο aResult
procedure TFileReadAndSum.ShowResult(aResult: Integer);
begin
Writeln( 'File Summary Result : ' + IntToStr(aResult) );
end;

// Πρόσθεση της παρούσας τιμής του αθροιστή aCrrentValue με το περιεχόμενο της γραμμής στην
// παράμετρο aLineNumber.
function TFileReadAndSum.SumVal(aCurrentValue: Integer; aLineNumber: Integer): Integer;
begin
Result := aCurrentValue + StrToInt(ReadLine(aLineNumber));
end;



// Όσο κι αν σας φανεί περίεργο,, το ουσιαστικό πρόγραμμά μας είναι αυτό που ακολουθεί! Αυτά που
// γράψαμε πριν έχουν νόημα μόνο αν συνδεθούν μεταξύ τους. Έτσι φροντίζουμε εδω να κάνουμε αυτούς
// τους συσχετισμούς ώστε η λογική μας να πάρει σάρκα και οστά ....

var // ΤΜΗΜΑ ΔΗΛΩΣΗΣ ΜΕΤΑΒΛΗΤΩΝ
LineIndex : Integer; // Δείκτης αριθμού γραμμής
Total : Integer; // Κράτηση συνόλου αθροίσματος
ReadAndSumObject: TFileReadAndSum; // Μεταβλητή αντιγράφου αντικειμένου
begin
ReadAndSumObject := TFileReadAndSum.Create(nil); // Δημιουργία αντιγράφου αντικειμένου
ReadAndSumObject._OpenFile; // Ανοίγουμε το αρχείο ...
Total := 0; // Μηδενίζουμε τον αθροιστή.
For LineIndex := 1 to 5 do // Για κάθε γραμμή από 1-5 του αρχείου
Total := ReadAndSumObject.SumVal(Total, LineIndex); // αθροίζουμε αντίστοιχα όπως ορίσαμε...

ReadAndSumObject.ShowResult( Total ); // Εμφάνιση του συνόλου στην οθόνη.
ReadAndSumObject._CloseFile; // Κλείσιμο αρχείου InputFile.txt
FreeAndNil(ReadAndSumObject); // Διαγραφή αντιγράφου αντικειμένου

Readln; // αναμονή για Enter
end. // Τερματισμός προγράμματος!!


Ωωωωωραίαααα!! Υπάρχει βέβαια ένα μικρό ζήτημα εδώ: Υποθέτουμε πως όλα θα πάνε ρολόι και οτι δεν θα υπάρχουν λάθη μέσα στο αρχείο. Πχ αντί για 12345 να διαβάσουμε κανένα 123ΨΔ. Όπως είναι αντιληπτό,, δεν υπάρχει φυσικά και περίπτωση να αθροιστεί αυτή η τιμή σαν αριθμός. Κάτι που θα καταλήξει σε σφάλμα. Γι’αυτό το λόγο υπάρχει και μια αντίστοιχη διαδικασία «διαχείρισης σφαλμάτων». Μια μικρή αλλαγή που μπορούμε να κάνουμε είναι να «προστατέψουμε» το ουσιαστικό προγραμμά μας κάπως έτσι:

Begin
Try
ReadAndSumObject := TFileReadAndSum.Create(nil);
ReadAndSumObject._OpenFile;
Total := 0;
For LineIndex := 1 to 5 do
Total := ReadAndSumObject.SumVal(Total, LineIndex);

ReadAndSumObject.ShowResult( Total );
ReadAndSumObject._CloseFile;
FreeAndNil(ReadAndSumObject);
Except
On Ex: Exception do
WriteLn(‘Πρόβλημα κατά την ανάγνωση/υπολογισμό με μήνυμα: ‘ + Ex.Message);
End;
Readln;
end.

Βάλαμε δηλαδή όλο το πρόγραμμα μέσα σε ένα block try…except…end όπου το λάθος διοχετεύεται κάτω από το Except. Το Except ερχεται από τη λέξη Exception που σημαίνει «εξαίρεση». Το σφάλμα δηλαδή διοχετεύεται στην απόληξη Except για περεταίρω επεξεργασία. Εμείς, με τη σειρά μας, το επεξεργαζόμαστε δίχνοντάς το στην οθόνη.

Πλέον όλα είναι στη θέση τους. Ο κώδικας γράφτηκε, το πρόγραμμα τρέχει αλλά εσείς?! Πως μπορείτε να το δείτε να τρέχει? Φυσικά όπως αντιλαμβάνεστε υπο κανονικές συνθήκες θα έπρεπε να προμηθευτείτε τη γλώσσα προγραμματισμού με κάποιο ... νόμιμο τρόπο φυσικά!! Παρόλα ταύτα οι εταιρείες έχουν και ελεύθερες εκδόσεις που δίνουν στον κόσμο δωρεαν. Οπότε μπορείτε να εγκαταστήσετε την Turbo Delphi από την Embarcadero σε αυτό το site https://downloads.embarcadero.com/free/turbodelphi . Το μέγεθος είναι 325.1 ΜΒ οπότε μπορεί να σας πάρει λίγη ώρα μέχρι να κατέβει. Εγκαταστήστε το, δημιουργείστε ένα καινούριο Console Application Project και αντιγράψτε το κώδικα του άρθρου. Πατήστε Ctrl+F9 για μεταγλώτιση και έλεγχο σφαλμάτων και F9 για να τρέξει. Αυτό είναι και το πρώτο σας ολοκληρωμένο και χρήσιμο πρόγραμμα! Προσπαθήστε να κάνετε αλλαγές ορίζοντας περισσότερες γραμμές για άθροιση στο αρχείο κλπ. Μεταγλωστίστε πάλι τρέξτε το και δείτε τις διαφοροποιήσεις.

Δεν υπάρχουν σχόλια:

Δημοσίευση σχολίου