Ταχεία εισαγωγή στον προγραμματισμό υπολογιστών
με τη γλώσσα qbasic

Μιχάλης Κολουντζάκης

Τμήμα Μαθηματικών
Πανεπιστήμιο Κρήτης
Λεωφόρος Κνωσού
714 09 Ηράκλειο

E-mail: kolount AT gmail.com
Φθινοπωρινό εξάμηνο 2009-10


Περιεχόμενα

Πρόλογος

Η γλώσσα προγραματισμού qbasic

Προσπαθούμε να δώσουμε τις βασικές αρχές προγραμματισμού μέσα σε τρεις δίωρες διαλέξεις.

Η γλώσσα που χρησιμοποιείται είναι η qbasic. Η "αρχαία" αυτή γλώσσα επελέγη εν πολλοίς επειδή είναι ταυτόχρονα εύχρηστη και το περιβάλλον προγραμματισμού της δεν είναι πολύ φορτωμένο όπως τα περισσότερα περιβάλλοντα προγραμματισμού σήμερα. Πραγματικά το περιβάλλον προγραμματισμού της qbasic περιλαμβάνει ότι χρειάζεται για αυτά που θέλουμε να μάθουμε σε αυτές τις 6 ώρες χωρίς να είναι υπερφορτωμένο (π.χ. όπως αυτό το περιβάλλον μιας σύγχρονης BASIC). Τέλος, η qbasic διατίθεται δωρεάν και μπορεί να τρέξει σχεδόν σε οποιοδήποτε σημερινό υπολογιστή, με οποιοδήποτε λειτουργικό σύστημα (αρκεί να διαθέτει ένα DOS emulator).

Είναι μια εξελιγμένη μορφή (1985) της γλώσσας BASIC (Beginner's All-purpose Symbolic Instruction Code) η οποία υπάρχει από to 1964 και για πάρα πολλά χρόνια υπήρξε η πιο διαδεδομένη γλώσσα προγραμματισμού για μικρούς "οικιακούς" υπολογιστές και ήταν η γλώσσα με την οποία πάρα πολλά άτομα της δικιάς μου γενιάς έμαθαν να προγραμματίζουν υπολογιστές. (Όχι όμως εγώ, που έμαθα να προγραμματίζω σε ένα Apple IIe με τη γλώσσα PASCAL σε ένα σεμινάριο του Πανεπιστημίου Κρήτης το 1983 σε μαθητές Λυκείου.)

Πώς να εγκαταστήσετε την qbasic στον υπολογιστή σας

Η γλώσσα qbasic (για να είμαστε λίγο πιο ακριβείς, ο interpreter για τη γλώσσα qbasic, το πρόγραμμα δηλ. που διαβάζει το πρόγραμμα qbasic που γράφουμε και το εκτελεί) γράφτηκε για το λειτουργικό σύστημα DOS, τη δεκαετία του 1980. Το DOS δεν χρησιμοποιείται πια αλλά πολλά προγράμματα που γράφτηκαν κάποτε για το DOS μπορούμε να τα τρέχουμε σήμερα στα μοντέρνα λειτουργικά συστήματα (π.χ. Windows XP, Linux) χρησιμοποιώντας κάποιον προσομοιωτή (emulator).

Ξεκινείστε λοιπόν κατεβάζοντας τα τρία αρχεία που θα βρείτε εδώ και αποθηκεύσετέ τα σε ένα directory (κατάλογο) του συστήματός σας, κατά προτίμηση ένα directory που έχετε δημιουργήσει ειδικά για αυτό το σκοπό και είναι αρχικά κενό.

Windows XP

Περιηγηθείτε στον υπολογιστή σας με τον Windows Explorer και πηγαίνετε στον κατάλογο όπου είναι τα αρχεία αυτά. Κάντε διπλό κλικ στο QBASIC.EXE για να τρέξετε το πρόγραμμα.

Linux

Για να τρέξετε την qbasic σε λειτουργικό σύστημα Linux πρέπει πρώτα να εγκαταστήσετε (αν δεν υπάρχει ήδη εγκατεστημένος) έναν DOS emulator (ένα πρόγραμμα δηλ. που ποσομοιώνει ένα υπολογιστή που τρέχει το παλιό λειτουργικό σύστημα DOS) όπως π.χ. τον dosemu. Πρέπει συνεπώς να εγκαταστήσετε πρώτα το «πακέτο» dosemu. Αυτό γίνεται συνήθως από τον package manager του συστήματος. Για παράδειγμα, αν το Linux που χρησιμοποιείτε είναι το Ubuntu (ή άλλο που βασίζεται στο Debian) τότε αρκεί να δώσετε την εντολή

sudo apt-get install dosemu
και να δώσετε και το root (administrator) password αν σας ζητηθεί.

Αφού εγκαταστήσετε το dosemu το τρέχετε (στο command line δίνετε xdosemu) και πηγαίνετε μέσα από το προσομοιωμένο DOS στον κατάλογο όπου έχετε αποθκεύσει την qbasic. Εκεί δίνετε την εντολή qbasic.

1 Διάλεξη Πρώτη - 4 Δεκ. 2009

1.1 Το πρόγραμμα sq.bas για υπολογισμό τετραγωνικής ρίζας



   1 CLS
   2 PRINT "Give me a number: "
   3 INPUT n
   4 PRINT SQR(n)

Το αρχείο sq.bas


Ένα πρόγραμμα στη γλώσσα BASIC και στις περισσότερες άλλες γλώσσες προγραμματισμού είναι μια ακολουθία από εντολές οι οποίες εκτελούνται από τον υπολογιστή η μια μετά την άλλη. Στο πρόγραμμα sq.bas που φαίνεται παραπάνω υπάρχουν 4 εντολές, μια σε κάθε γραμμή (οι αριθμοί μπροστά από τις γραμμές δεν υπάρχουν πραγματικά στο πρόγραμμα αλλά τις έχουμε βάλει μέσα για να μπορούμε να αναφερόμαστε στις γραμμές με κάποιο τρόπο).

Η εντολή CLS καθαρίζει την οθόνη του υπολογιστή και τη χρησιμοποιούμε ως πρώτη εντολή σχεδόν σε κάθε πρόγραμμα από αυτά που θα γράψουμε. Στη γραμμή 2 η εντολή PRINT τυπώνει στην οθόνη το μήνυμα που ακολουθεί εντός εισαγωγικών. Το μήνυμα αυτό προτρέπει το χρήστη του προγράμματος να δώσει ένα αριθμό (πληκτρολογώντας τον στο πληκτρολόγιο). Ο αριθμός αυτός διαβάζεται από τον υπολογιστή στην επόμενη εντολή INPUT n, και τοποθετείται στη μεταβλητή n.

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

Τέλος στη γραμμή 4 εκτυπώνεται στην οθόνη η τετραγωνική ρίζα του αριθμού που έδωσε ο χρήστης, η οποία υπολογίζεται από την έκφραση SQR(n).

Στο πρόγραμμα αυτό υπάρχουν κάποιες λέξεις που είναι δεσμευμένες από την BASIC. Αυτές οι λέξεις έχουν ειδικό νόημα για τη γλώσσα και δεν επιτρέπεται να τις χρησιμοποιήσουμε αλλιώς, π.χ. ως ονόματα μεταβλητών. Οι δεσμευμένες λέξεις που εμφανίζονται στο πρόγραμμα αυτό είναι οι CLS, PRINT, INPUT, SQR. Οι τρεις πρώτες είναι εντολές (για καθάρισμα οθόνης, γράψιμο στην οθόνη και διάβασμα από το πληκτρολόγιο αντίστοιχα) και η τελευταία είναι συνάρτηση (της δίνουμε ένα αριθμό και μας επιστρέφει την τετραγωνική του ρίζα).

Άσκηση 1.Αλλάξτε την γραμμή 4 σε: PRINT SQR(n), n*n. Τι κάνει τώρα το πρόγραμμα;

Άσκηση 2.Γράψτε ένα πρόγραμμα το οποίο να διαβάζει από το χρήστη τα μήκη των δύο κάθετων πλευρών ορθογωνίου τριγώνου και να υπολογίζει το μήκος της υποτείνουσας.

1.2 Το πρόγραμμα grades.bas για εύρεση τελικού βαθμού



   1 CLS
   2 
   3 ' Data entry below
   4 
   5 t1input:
   6 
   7 PRINT "Give T1: "
   8 INPUT t1
   9 IF (t1 < 0) OR (t1 > 10) THEN
  10  PRINT "Error"
  11  GOTO t1input
  12 END IF
  13 
  14 t2input:
  15 
  16 PRINT "Give T2: "
  17 INPUT t2
  18 IF (t2 < 0) OR (t2 > 10) THEN
  19  PRINT "Error"
  20  GOTO t2input
  21 END IF
  22 
  23 finput:
  24 
  25 PRINT "Give F: "
  26 INPUT f
  27 IF (f < 0) OR (f > 10) THEN
  28  PRINT "Error"
  29  GOTO finput
  30 END IF
  31 
  32 'First case
  33 IF f < 3 THEN
  34  PRINT "Sorry, next time."
  35  END
  36 END IF
  37 
  38 'Second case
  39 f1 = (2 / 3) * f + t1 / 6 + t2 / 6
  40 IF f1 > f THEN
  41  grade = f1
  42 ELSE
  43  grade = f
  44 END IF
  45 
  46 ' output
  47 PRINT "Your grade is", grade

Το αρχείο grades.bas


Κάποιος καθηγητής ακολουθεί το εξής βαθμολογικό σύστημα σε ένα μάθημά του: οι φοιτητές του γράφουν δύο ενδιάμεσα διαγωνίσματα και ένα τελικό διαγώνισμα, έστω τους βαθμούς $T_1$, $T_2$ και $F$. Αν $F\ge 3$ τότε ο βαθμός είναι

\begin{displaymath}
\max{\left\{{F, \frac{2}{3}F + \frac{1}{6}T_1 + \frac{1}{6}T_2}\right\}},
\end{displaymath} (1)

αλλιώς ο βαθμός είναι 0.

Το παραπάνω πρόγραμμα υπολογίζει τον τελικό βαθμό δοθέντων των $T_1$, $T_2$ και $F$.

Τι καινούργια στοιχεία υπάρχουν εδώ;

Κατ' αρχήν οι γραμμές με τα σχόλια, αυτές δηλ. που αρχίζουν με το χαρακτήρα ' (single quote), στις γραμμές 3, 32, 38, 46. Οι γραμμές αυτές παίζουν μόνο διακοσμητικό ρόλο μέσα στο πρόγραμμα και ο υπολογιστής τις αγνοεί. Συνεισφέρουν όμως τα μέγιστα στο να είναι ένα πρόγραμμα ευανάγνωστο, και είναι ένας από τους σημαντικότερους κανόνες καλού προγραμματισμού (και σίγουρα αυτός που παραβιάζεται συχνότερα, πάνω στη βιασύνη μας) το να έχει ένα πρόγραμμα αρκετά σχόλια γραμμένα μέσα του. Είναι πολύ συνηθισμένο να διαβάζει κανείς προγράμματα που έχει γράψει ο ίδιος πριν από μερικά χρόνια και, επειδή δεν είχε τότε κάνει τον κόπο να γράψει αρκετά σχόλια μέσα, να μην τα καταλαβαίνει.

Το επόμενο νέο στοιχείο είναι οι ετικέτες (labels) στις γραμμές 5, 14, 23. Οι ετικέτες δεν είναι ακριβώς εντολές του προγράμματος. Αποτελούν ουσιαστικά την ονομασία μιας θέσης μέσα στο πρόγραμμα. Για παράδειγμα η ετικέτα t1input: περιγράφει τη θέση του προγράμματος αμέσως πριν την εντολή PRINT "Give T1: ". Όταν εκτελεστεί από τον υπολογιστήη γραμμή 11, GOTO t1input, τότε ο έλεγχος (η τρέχουσα θέση δηλαδή του προγράμματος) μεταβιβάζεται αμέσως στη θέση t1input: και έτσι η επόμενη εντολή που εκτελείται είναι η γραμμή 7.

Το επόμενο σοβαρό νέο στοιχείο που βλέπουμε είναι η σύνθετη εντολή IF ... THEN ... END IF καθώς και η IF ... THEN ... ELSE ... END IF (εκτέλεση εντολών υπό συνθήκη).

Ας πάρουμε π.χ. τις γραμμές 33-36. Οι εντολές στις γραμμές 34, 35 εκτελούνται μόνο αν η συνθήκη του IF, δηλ. το f<3 είναι αληθής (στη μεταβλητή f αποθηκεύουμε το βαθμό του τελικού διαγωνίσματος που μας έχει δώσει ο χρήστης στις γραμμές 25-30). Σε αυτή την περίπτωση εκτυπώνεται ένα μήνυμα (Sorry, next time.) και το πρόγραμμα σταματάει (εντολή END).

Ας δούμε λίγο τι γίνεται στις γραμμές 5-12 (και παρόμοια στις γραμμές 14-21, όπου διαβάζεται από το χρήστη η μεταβλητή t1, και στις γραμμές 23-30, όπου διαβάζεται η f). Στη γραμμή 7 τυπώνεται ένα μήνυμα και στην 8 διαβάζουμε από τη πληκτρολόγιο το βαθμού του πρώτου διαγωνίσματος $T_1$ και τον αποθηκεύουμε στη μεταβλητή t1. Στις γραμμές 9-12 ελέγχουμε αν η τιμή της t1 είναι εκτός ορίων (είναι δηλ. <0 ή >10) και σε αυτή την περίπτωση τυπώνουμε ένα μήνυμα (Error) και αμέσως μετά, με την εντολή GOTO t1input), o έλεγχος μεταβιβάζεται στη γραμμή 7, και έτσι ο χρήστης έχει την ευκαιρία να διορθώσει το λάθος του και να δώσει μια τιμή για το $T_1$ εντός των επιτρεπτών ορίων. Αν και πάλι αποτύχει το πρόγραμμα τον ξαναστέλνει πίσω, επ' άπειρον.

Στη γραμμή 39 υπολογίζουμε στη μεταβλητή f1 την τιμή της έκφρασης $\frac{2}{3}F + \frac{1}{6}T_1 + \frac{1}{6}T_2$.

Στο IF ... THEN ... ELSE ... END IF των γραμμών 40-44 κοιτάμε αν f1>f και σε αυτή την περίπτωση βάζουμε στη μεταβλητή grade την τιμή της f1, αλλιώς της βάζουμε την τιμή της f.

Πρέπει επίσης να κάνουμε την παρατήρηση εδώ ότι όταν ο έλεγχος φτάσει στη γραμμή 39 είμαστε σίγουροι ότι δεν ισχύει f<3 αφού σε αυτή την περίπτωση το πρόγραμμα θα είχε τελειώσει με την εκτέλεση της γραμμής 35.

Τέλος, στη γραμμή 47, τυπώνουμε δύο πράγματα. Το κείμενο (string) "Your grade is" (χωρίς τα εισαγωγικά, τα οποία δεν τυπώνουνται και βρίσκονται εκεί μόνο για να υποδηλώσουν τα όρια του string) ακολουθούμενο από ένα αριθμό, την τιμή της μεταβλητής grade. Τυπώνονται το ένα δίπλα στο άλλο χωρίς να αλλάξει γραμμή ανάμεσά τους.

Άσκηση 3.Τροποποιείστε το πρόγραμμα grades.bas ώστε να υπολογίζει τον τελικό βαθμό αν αυτός δίδεται από τον τύπο (1), αλλά μόνο στην περίπτωση που όλοι οι βαθμοί $T_1, T_2, F$ είναι τουλάχιστον 3, αλλιώς ο τελικός βαθμός είναι 0.

1.3 Το πρόγραμμα q.bas για την επίλυση της τετραγωνικής εξίσωσης



   1 CLS
   2 
   3 start:
   4 
   5 ' data input
   6 PRINT "Give the coefficients A, B, C: "
   7 INPUT a, b, c
   8 
   9 ' linear equation
  10 IF a = 0 THEN
  11  IF NOT (b = 0) THEN
  12   PRINT "Single root: ", -c / b
  13   GOTO check
  14  ELSE
  15   IF c = 0 THEN
  16    PRINT "All real numbers are roots."
  17    GOTO check
  18   ELSE
  19    PRINT "No roots."
  20    GOTO check
  21   END IF
  22  END IF
  23 END IF
  24 
  25 ' quadratic equation
  26 d = b ^ 2 - 4 * a * c
  27 IF d < 0 THEN
  28  PRINT "No roots (discriminant ="; d; ")"
  29  GOTO check
  30 END IF
  31 
  32 IF d = 0 THEN
  33  PRINT "One double root: "; -b / (2 * a)
  34  GOTO check
  35 END IF
  36 
  37 ' Here the discriminant is positive
  38 r1 = (-b + SQR(d)) / (2 * a)
  39 r2 = (-b - SQR(d)) / (2 * a)
  40 
  41 PRINT "Two roots: "; r1; " and "; r2
  42 
  43 check:
  44 
  45 PRINT "Give 1 to continue, 0 to stop: "
  46 INPUT ans
  47 IF ans = 1 THEN
  48  GOTO start
  49 END IF

Το αρχείο q.bas


Το άνω πρόγραμμα διαβάζει από το χρήστη τους συντελεστές $A, B, C$ της γενικής τετραγωνικής εξίσωσης

\begin{displaymath}
A x^2 + B x + C = 0
\end{displaymath}

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

Αυτή η επανάληψη του προγράμματος επιτυγχάνεται με τις γραμμές 43-49 που αρχίζουν με την ετικέτα check:. Στη γραμμή 49 ρωτάμε το χρήστη αν θέλει να συνεχίσει. Πρέπει να απαντήσει με τον αριθμό 1 αν ναι και με τον αριθμό 0 αν όχι. Διαβάζουμε την απάντησή του στη μεταβλητή ans και στο IF που ακολουθεί ελέγχουμε αν έχει δώσει 1, και αν ναι ξαναπηγαίνουμε στην αρχή του προγράμματος (ετικέτα start:). Αν όχι τότε το πρόγραμμα τελειώνει εφόσον αυτό το IF είναι και η τελευταία (σύνθετη) εντολή στο πρόγραμμα.

Έχοντας βάλει την ετικέτα start κάτω από την εντολή CLS επιτυγχάνουμε να μην καθαρίζει η οθόνη σε κάθε επανάληψη του προγράμματος μια και ο χρήστης ίσως να θέλει να βλέπει και τα αποτελέσματα της προηγούμενης επίλυσης. Αν θέλαμε να καθαρίζει η οθόνη σε κάθε επανάληψη τότε θα βάζαμε την ετικέτα start: πριν την εντολή CLS.

Στις γραμμές 6 και 7 διαβάζουμε τους συντελεστές της εξίσωσης και τους βάζουμε στις μεταβλητές a, b, c. Παρατηρείστε ότι χρησιμοποιούμε την εντολή INPUT μόνο μια φορά και όχι τρεις, αλλά με τρία ορίσματα (έχει αυτή τη δυνατότητα, να παίρνει δηλ. περισσότερα του ένα ορίσματα).

Στις γραμμές 10-23 αντιμετωπίζουμε την περίπτωση $A=0$ οπότε έχουμε να λύσουμε τη γραμμική εξίσωση $Bx+C=0$. Όλες οι εντολές αυτής της ομάδας βρίσκονται μέσα στο IF της γραμμής 10 και εκτελούνται συνεπώς μόνο αν η μεταβλητή a έχει την τιμή 0.

Στις γραμμές 12 και 13 αντιμετωπίζουμε την περίπτωση όπου $B \neq 0$. Το αν αυτό ισχύει το ελέγχουμε με τη συνθήκη NOT (b=0) η οποία αληθεύει αν και μόνο αν δεν αληθεύει η συνθήκη b=0. Θα μπορούσαμε να είχαμε γράψει τη συνθήκη NOT (b=0) και ως b <> 0, μια και ο τελεστής <> σημαίνει διάφορο στην qbasic (και σε πολλές άλλες γλώσσες προγραμματισμού). Αν λοιπόν $B \neq 0$ τότε στη γραμμή 12 τυπώνουμε τη μοναδική ρίζα της εξίσωσης $-C/B$ και πηγαίνουμε με τη γραμμή 13 στο τέλος του προγράμματος. Δε δίνουμε όμως την εντολή END για να τελειώσουμε το πρόγραμμα εδώ αλλά πηγαίνουμε στο block γραμμών που αρχίζουν με την ετικέτα check: όπου και ρωτάμε το χρήστη αν θέλει να συνεχίσει να λύνει εξισώσεις ή θέλει να σταματήσει. Αν $B=0$ και $C=0$ (έλεγχος στη γραμμή 15) τότε κάθε πραγματικός αριθμός είναι λύση της εξίσωσης και αναφέρουμε αυτό στη γραμμή 16, αλλιώς (δηλ. είμαστε τώρα στην περίπτωση $B=0, C\neq 0$) δεν υπάρχει λύση, το οποίο αναφέρουμε στη γραμμή 19.

Παρατηρείστε τα IF σε διάφορα επίπεδα (nested) στις γραμμές 10-23. Το IF της γραμμής 10 κλείνει στη γραμμή 23, ενώ στις γραμμές 11, 14, 22 έχουμε ένα IF ...THEN ... ELSE ... END IF, και στις γραμμές 15, 18, 21 άλλο ένα τέτοιο. Προσέξτε επίσης ότι όταν γράφουμε ένα τέτοιο σύνθετο block εντολών καλό είναι να στοιχίζουμε τις εντολές (να τις γράφουμε δηλ. αριστερότερα ή δεξιότερα στη γραμμή τους) με τέτοιο τρόπο ώστε να κάνουμε τη δομή του προγράμματος πιο εμφανή, πράγμα που βοηθάει πολύ στην κατανόηση του κώδικα. Υπάρχουν διάφοροι τρόποι να γίνει αυτό και το ποιον τρόπο ακολουθεί κανείς είναι και θέμα αισθητικής του καθενός. Ο τρόπος που ακολουθώ εδώ είναι ότι όταν γράφω ένα IF ... ELSE ... END IF τότε φροντίζω αυτές οι 3 λέξεις να είναι στοιχισμένες στην ίδια στήλη ενώ όσες εντολές είναι ανάμεσά τους στοιχίζονται μια θέση δεξιότερα.

Από τη γραμμή 25 και κάτω είμαστε πλέον σίγουροι ότι $A \neq 0$ μια και αν ήταν 0 θα το είχε «πιάσει» το IF της γραμμής 10 και δε θα έφτανε ποτέ ο έλεγχος στη γραμμή 25. Στη γραμμή 26 υπολογίζουμε τη διακρινουσα (discriminant) της εξίσωσης $D = B^2 - 4AC$. Στην qbasic η έκφραση a^b παριστάνει το $a^b$ (και πρέπει φυσικά να ισχύει τη στιγμή του υπολογισμού της έφρασης ότι $a>0$, αλλιώς ο υπολογιστής βγάζει μήνυμα σφάλματος). Θα μπορούσαμε φυσικά να είχαμε υπολογίσει τη διακρίνουσα με την εντολή d = b*b-4*a*c (που είναι και πιο γρήγορη στην εκτέλεσή της).

Στις γραμμές 27-30 χειριζόμαστε την περίπτωση αρνητικής διακρίνουσας και στις γραμμές 32-35 την περίπτωση που η διακρίνουσα είναι 0. Στην πρώτη περίπτωση τυπώνουμε στη γραμμή 28 ένα μήνυμα που λέει ότι δεν έχουμε ρίζες λόγω αρνητικής διακρίνουσας και λέμε και πόσο είναι η διακρίνουσα. Στη δεύτερη περίπτωση τυπώνουμε τη μοναδική (διπλή) ρίζα της εξίσωσης $-B/(2A)$. Η εντολή PRINT της γραμμής 28 παίρνει τρία ορίσματα: το κείμενο (string) "No roots (discriminant =", τον αριθμό d και το κείμενο ")". Αυτά τα διαχωρίζουμε με το χαρακτήρα ; και όχι με κόμα για να μην παρεμβάλονται μεγάλα κενά ανάμεσά τους κατά την εκτύπωσή τους. Έτσι αυτό που τυπώνεται μοιάζει, π.χ., με

No roots (discriminant =-3.4 )

Στις γραμμές 38 και 39 είμαστε σίγουροι ότι η διακρίνουσα είναι θετική και υπολογίζουμε τις δύο ρίζες

\begin{displaymath}
r_1 = \frac{-B+\sqrt{B^2-4AC}}{2A}, r_2 = \frac{-B-\sqrt{B^2-4AC}}{2A},
\end{displaymath}

στις μεταβλητές r1 και r2. Στη γραμμή 41 τυπώνουμε αυτές τις δύο ρίζες.

Άσκηση 4. Γράψτε ένα πρόγραμμα που να λύνει ένα $2\times 2$ γραμμικό σύστημα

\begin{displaymath}
\left(\begin{array}{cc} a & b c & d \end{array}\right) \le...
...} \right)
= \left( \begin{array}{c} e  f\end{array} \right)
\end{displaymath} (2)

ως προς τους αγνώστους $x, y$, το σύστημα δηλ. των δύο εξισώσεων

\begin{displaymath}
ax+by=e, cx+dy=f.
\end{displaymath}

Τα δεδομένα που θα πρέπει να διαβάζει το πρόγραμμά σας από το χρήστη είναι οι αριθμοί $a, b, c, d, e, f$ και θα πρέπει να βρίσκει όλα τα ζεύγη $(x, y)$ που ικανοποιούν το σύστημα (2).

Αρχίστε υπολογίζοντας τις ορίζουσες των πινάκων

\begin{displaymath}
\left(\begin{array}{cc} a & b c & d \end{array}\right),
\l...
...ht),
\left(\begin{array}{cc} a & e c & f \end{array}\right).
\end{displaymath}

2 Διάλεξη Δεύτερη - 11 Δεκ. 2009

2.1 Το πρόγραμμα list.bas για πράξεις σε μια λίστα αριθμών



   1 ' some declarations of variables
   2 DIM n AS INTEGER
   3 DIM a(100) AS INTEGER
   4 
   5 
   6 CLS
   7 
   8 ' read the length of the list of numbers
   9 ninput:
  10 PRINT "How many numbers? "
  11 INPUT n
  12 IF 0 > n OR n > 100 THEN
  13  PRINT "Bad number."
  14  GOTO ninput
  15 END IF
  16 
  17 ' input the numbers
  18 FOR i = 1 TO n
  19  PRINT "Give me number No "; i; ": "
  20  INPUT a(i)
  21 NEXT
  22 
  23 ' compute the sum
  24 s = 0
  25 FOR i = 1 TO n
  26  s = s + a(i)
  27 NEXT
  28 PRINT "The sum is"; s
  29 
  30 ' compute the maximum and the minimum number
  31 mm = a(1)
  32 m = a(1)
  33 FOR i = 2 TO n
  34  IF a(i) > mm THEN
  35   mm = a(i)
  36  ELSEIF m > a(i) THEN
  37   m = a(i)
  38  END IF
  39 NEXT
  40 PRINT "The maximum is "; mm; " and the minimum is "; m

Το αρχείο list.bas


Το πρόγραμμα αυτό διαβάζει μια λίστα από αριθμούς, τους οποίους δίνει ο χρήστης του από το πληκτρολόγιο, υπολογίζει το άθροισμά τους καθώς και το μέγιστο και τον ελάχιστό τους, και τα τυπώνει.

Είναι η πρώτη φορά που βλέπουμε μια ανακύκλωση (loop) να υλοποείται με τη σύνθετη εντολή FOR i=1 TO ... NEXT.

Επίσης, στις γραμμές 2-3 βλέπουμε, για πρώτη φορά, δύο δηλώσεις μεταβλητών. Στη γραμμή 2 δηλώνεται η μεταβλητή n ως μεταβλητή ακεραίου τύπου (INTEGER). Από δω και πέρα αν επιχειρήσουμε να δώσουμε μια εντολή όπως η n = 1.2 που δεν είναι συμβατή με αυτό το είδος μεταβλητής θα προκληθεί σφάλμα. Οι δηλώσεις μεταβλητών, αν και συνήθως περιττές για τα πολύ απλά προγράμματα σε qbasic (ενώ σε άλλες γλώσσες όπως η Pascal και η C είναι υποχρεωτικές) είναι πολύ καλή τακτική προγραμματισμού γιατί μας προφυλάσσουν από το να κάνουμε εμείς λάθη. Πολλά λάθη του προγραμματιστή "πιάνονται" αν έχουν δηλωθεί οι τύποι των μεταβλητών κατ' αρχήν, χώρια που τα προγράμματα είναι πιο αποτελεσματικά (τρέχουν πιο γρήγορα).

Στη γραμμή 3 βλέπουμε μια δήλωση της μεταβλητής a ως ένας πίνακας (array) από ακεραίους μήκους 100. Είναι σα να έχουμε δηλώσει ταυτόχρονα 100 μεταβλητές των οποίων τα ονόματα είναι τα a(1), a(2), ... , a(100). Οι πίνακες είναι απολύτως απαραίτητα κατασκευάσματα ακόμη και για τα απλούστερα προγράμματα που μπορεί να θέλει κανείς να γράψει.

Στις γραμμές 9-15 ερωτάται ο χρήστης για το πόσους αριθμούς θα έχει η λίστα του. Το πλήθος αυτό αποθηκεύεται στη μεταβλητή n, και το πρόγραμμα ελέγχει αν η τιμή που έδωσε ο χρήστης είναι νόμιμη: το n δε μπορεί να είναι φυσικά αρνητικό αλλά ούτε και μεγαλύτερο του 100, αφού η λίστα που θα δώσει μετέπειτα ο χρήστης θα αποθηκευθεί στις θέσεις του πίνακα a. Αν δεν είναι νόμιμη η τιμή τότε επαναλαμβάνεται η ερώτηση έως ότου η τιμή είναι αποδεκτή. Αυτό επιτυγχάνεται με το GOTO ninput.

Στις γραμμές 18-21 ο χρήστης δίνει τις n αυτές τιμές. Το FOR loop εκτελείται για όλες τις τιμές i=1,2,...,n και κάθε φορά τυπώνεται ένα μήνυμα στο χρήστη για να δώσει το αντίστοιχο στοιχείο. Αυτό μπαίνει στη θέση a(i). Την πρώτη φορά που θα εκτελεστεί η ανακύκλωση γεμίζει η μεταβλητή a(1), τη δεύτερη η μεταβλητή a(2), κλπ.

Σε αυτό το σημείο έχουμε διαβάσει τα δεδομένα μας και τα έχουμε αποθηκεύεσει στον πίνακα a.

Στις γραμμές 24-27 υπολογίζεται το άθροισμα των αριθμών στη μεταβλητή s και τυπώνεται στη γραμμή 28. Η μεταβλητή s τίθεται αρχικά ίση με το 0 και σε κάθε επανάληψη του FOR loop της προστίθεται η τιμή a(i). Αυτό είναι το νόημα της εντολής-ανάθεσης

s = s + a(i),
η οποία μοιάζει λίγο περίεργη σε όσους δεν έχουν εμπειρία από γλώσσες προγραμματισμού μια και τείνουν να ερμηνεύσουν το σύμβολο = ως σύμβολο ισότητας. Όμως στη γραμμή αυτή το = παίζει το ρόλο της ανάθεσης: υπολογίζεται η ποσότητα που παριστάνει το δεξί του μέλος και το αποτέλεσμα αποθηκεύεται στη μεταβλητή που φαίνεται στο αριστερό του μέλος. Το ότι η μεταβλητή αυτή (s στην προκείμενη περίπτωση) εμφανίζεται και στο δεξί μέλος δεν αποτελεί πρόβλημα. Η μεταβλητή έχει ήδη κάποια τιμή τη στιγμή του υπολογισμού του δεξιού μέλους. Μετά αυτή η τιμή αντικαθίσταται από το αποτέλεσμα αυτού του υπολογισμού.

Στις γραμμές 31-39 υπολογίζονται η ελάχιστη τιμή (στη μεταβλητή m) και η μέγιστη τιμή (στη μεταβλητή mm) της λίστας μας των αριθμών και τυπώνεται το αποτέλεσμα στη γραμμή 40. Κατ' αρχήν θέτουμε και τις δύο αυτές μεταβλητές ίσες με το a(1). Έπειτα χρησιμοποιούμε το FOR loop για i=2,3,...,n ώστε να συγκρίνουμε κάθε ένα από τα υπόλοιπα a(i) με την τρέχουσα τιμή του μέγιστου και του ελάχιστου. Αν, όταν εξετάζουμε την τιμή a(i) τη βρούμε μεγαλύτερο του τρέχοντος μεγίστου (γραμμή 34) τότε αναθέτουμε την τιμή αυτή στο τρέχον μέγιστο (γραμμή 35), αλλιώς αν βρούμε το a(i) μικότερο του τρέχοντος ελαχίστου (γραμμή 36) τότε αναθέτουμε την τιμή a(i) στο τρέχον ελάχιστο (γραμμή 37).

2.2 Το πρόγραμμα list2.bas για πράξεις σε μια λίστα αριθμών



   1 DIM n AS INTEGER
   2 DIM a(100) AS SINGLE
   3 
   4 
   5 CLS
   6 
   7 PRINT "Give me the numbers. Finish with 0."
   8 
   9 ' input the numbers
  10 i = 0
  11 readanumber:
  12 i = i + 1
  13 PRINT "Give me number No "; i; ": "
  14 INPUT a(i)
  15 IF a(i) = 0 THEN
  16  n = i - 1
  17 ELSE
  18  IF i < 100 THEN
  19   GOTO readanumber
  20  ELSE
  21   PRINT "Cannot read any more. Bye."
  22  END IF
  23 END IF
  24 
  25 ' compute the sum
  26 s = 0
  27 FOR i = 1 TO n
  28  s = s + a(i)
  29 NEXT
  30 PRINT "The sum is"; s
  31 
  32 ' compute the maximum and the minimum number
  33 mm = a(1)
  34 m = a(1)
  35 FOR i = 2 TO n
  36  IF a(i) > mm THEN
  37   mm = a(i)
  38  ELSEIF m > a(i) THEN
  39   m = a(i)
  40  END IF
  41 NEXT
  42 PRINT "The maximum is "; mm; " and the minimum is "; m

Το αρχείο list2.bas


Το πρόγραμμα list2.bas είναι μια διαφορετική (βελτιωμένη) έκδοση του list.bas. Η σημαντική διαφορά τους είναι ότι το πρόγραμμα list2.bas έχει γραφτεί με τέτοιο τρόπο ώστε ο χρήστης να μη χρειάζεται να δίνει εκ των προτέρων το μήκος της λίστας των αριθμών που πρόκειται να ακολουθήσει. Αντί γι' αυτό κάνουμε την υπόθεση (περιορισμός του προγράμματος αν θέλετε) ότι κανείς από τους αριθμούς της λίστας δεν είναι 0, και αν ο χρήστης δώσει 0 τότε απλά σηματοδοτεί με αυτό τον τρόπο το τέλος της λίστας. Δε χρειάζεται έτσι να ξέρουμε εξ αρχής το μήκος της λίστας (μπορεί κι ο χρήστης να μη το γνωρίζει) και απλά το υπολογίζουμε από το πόσους αριθμούς έδωσε ο χρήστης μέχρι να δώσει 0.

Μια άλλη αλλαγή που έχουμε κάνει είναι ότι τώρα οι αριθμοί που διαβάζουμε δεν είναι εν γένει ακέραιοι αλλά πραγματικοί αριθμοί. Αυτό είναι το νόημα του τύπου SINGLE για τη μεταβλητή a(100).

Στις γραμμές 12-23 γίνεται λοιπόν η ανάγνωση της λίστας χρησιμοποιώντας ένα δείκτη i ο οποίος είναι τέτοιος ώστε ανά πάσα στιγμή διαβάζουμε το στοιχείο a(i). Στις γραμμές 13-14 ο χρήστης δίνει το a(i). Αν αυτό είναι 0 (γραμμές 15-16) τότε σταματάμε το διάβασμα και θέτουμε το μήκος της λίστας n ίσο με i-1 (αφού το τελευταίο στοιχείο που διαβάσαμε, το a(i) είναι το 0 και δε μετέχει στη λίστα σύμφωνα με τη σύμβαση που κάναμε.

Πρέπει φυσικά να προσέξουμε να μη διαβάσουμε περισσότερους αριθμούς απ΄ όσους μπορούμε να αποθηκεύσουμε, δηλ. 100. Ο έλεγχος αυτός γίνεται στις γραμμές 18-22. Το διάβασμα συνεχίζεται εφόσον έχουμε διαβάσει λιγότερους από 100 αριθμούς, αλλιώς τυπώνεται ένα μήνυμα, σταματάει το διάβασμα αριθμών και συνεχίζει το πρόγραμμα με τους υπολογισμούς που πρέπει να κάνει. Αυτοί γίνεται ακριβώς όπως και στο πρόγραμμα list.bas που σχολιάσαμε παραπάνω.

Άσκηση 5. Στα προγράμματα list.bas και list2.bas κρατήσαμε τα δεδομένα (τη λίστα αριθμών) σε ένα πίνακα κατά το διάβασμά τους από το χρήστη και κατόπιν τα επεξεργαστήκαμε από εκεί μέσα. Δεν είναι όμως απαραίτητο γι' αυτό το πρόγραμμα να κρατηθούν τα νούμερα αυτά στη μνήμη, αφού μπορούν όλα τα ζητούμενα να υπολογίστούν από τους αριθμούς που διαβάζουμε χωρίς να τους κρατάμε στη μνήμη, αλλά απλά χρησιμοποιώντας κάθε νέο αριθμό που διαβάζουμε για να ενημερώνουμε τις μεταβλητές s, mm και m που κρατάμε για τις τρέχουσες τιμές του αθροίσματος, του μέγιστους και του ελάχιστου αριθμού.

Γράψτε λοιπόν ένα πρόγραμμα που να κάνει τη δουλειά που κάνει το list.bas ή το list2.bas αλλά χωρίς να χρησιμοποιεί κάποιον πίνακα και χωρίς να αποθηκεύει τα δεδομένα του πριν τα επεξεργαστεί.

2.3 Τό πρόγραμμα reverse.bas για αντιστροφή της σειράς μιας λίστας αριθμών



   1 DIM a(1000) AS SINGLE
   2 DIM n AS INTEGER
   3 
   4 
   5 CLS
   6 
   7 PRINT "How many numbers?"
   8 INPUT n
   9 
  10 FOR i = 1 TO n
  11  PRINT "Give me number No"; i
  12  INPUT a(i)
  13 NEXT
  14                                     
  15 FOR i = n TO 1 STEP -1
  16  PRINT "A("; i; ")="; a(i)
  17 NEXT

Το αρχείο reverse.bas


Το πρόγραμμα reverse.bas διαβάζει n αριθμούς από το χρήστη και έπειτα τους τυπώνει με την ανάποδη σειρά. Το input των αριθμών γίνεται ακριβώς όπως και στο πρόγραμμα list.bas παραπάνω: ο χρήστης πρώτα δίνει το μήκος της λίστας (μεταβλητή n) και έπειτα δίνει τους n αριθμούς (γραμμές 7-13).

Στις γραμμές 15-17 τυπώνεται η λίστα με την ανάποδη σειρά, δηλ. οι αριθμοί a(n), a(n-1), ... , a(2), a(1). Αυτό το πετυχαίνουμε με ένα FOR loop, στο οποίο ο δείκτης i διατρέχει τις τιμές n έως 1, ξεκινώντας από το n και μειωνόμενος κάθε φορά κατά 1. Αυτό ακριβώς είναι το νόημα του STEP -1. Στην απλή μορφή του FOR loop, όταν η παράμετρος STEP παραλείπεται, αυτή θεωρείται πάντα ίση με +1.

Άσκηση 6. Φτιάξτε ένα πρόγραμμα που να διαβάζει μια λίστα αριθμών $a_1, a_2, a_3, \ldots, a_N$ από το χρήστη και μετά να τυπώνει στην οθόνη την ίδια λίστα όπου οι αριθμοί $a_1, a_3, a_5, \ldots$ (με περιττούς δείκτες δηλ.) να είναι στις θέσεις τους αλλά οι αριθμοί $a_2, a_4, a_6, \ldots$ να τυπώνονται με την ανάποδη σειρά. Προσέξτε τις περιπτώσεις όπου το $N$ είναι περιττό ή άρτιο.

2.4 Το πρόγραμμα sort.bas για διάταξη μιας λίστας αριθμών σε αύξουσα σειρά



   1 DIM a(1000), t AS SINGLE
   2 DIM n, i, j AS INTEGER
   3 
   4 
   5 CLS
   6 
   7 PRINT "How many numbers?"
   8 INPUT n
   9 
  10 FOR i = 1 TO n
  11  PRINT "Give me number No"; i
  12  INPUT a(i)
  13 NEXT
  14                                     
  15 FOR i = 1 TO n - 1
  16  FOR j = i + 1 TO n
  17   IF a(i) > a(j) THEN
  18    t = a(i)
  19    a(i) = a(j)
  20    a(j) = t
  21   END IF
  22  NEXT
  23 NEXT
  24 
  25 FOR i = 1 TO n
  26  PRINT a(i);
  27 NEXT

Το αρχείο sort.bas


Στο πρόγραμμα αυτό σκοπός μας είναι να διαβάσουμε μια λίστα από n αριθμούς (αυτό γίνεται στις γραμμές 7-13) και να τους τυπώσουμε αλλά σε αύξουσα σειρά. Για παράδειγμα, αν ο χρήστης δώσει ως input τους αριθμούς 3, 2, 1, 4, 4, 5, 2 τότε το output θα είναι 1, 2, 2, 3, 4, 4, 5. Το input γίνεται ακριβώς όπως στα προγράμματα list.bas και reverse.bas παραπάνω. Η μόνη διαφορά είναι ότι ο πίνακας a έχει 1000 θέσεις αντί για 100 (κι έτσι χωράει μεγαλύτερες λίστες).

Η μέθοδος που ακολουθούμε για την ταξινόμηση της λίστας αριθμών είναι το λεγόμενο bubble sort. Στη μέθοδο αυτή συγκρίνουμε συνεχώς μεταξύ τους ζεύγη θέσεων του πίνακα a, ας πούμε τις θέσεις i και j, με i<j, και αν τα περιεχόμενα τους δεν είναι στη σωστή σειρά, αν δηλ. a(i)>a(j) (γραμμή 17) τότε εναλλάσσουμε τα περιεχόμενά τους (γραμμές 18-20). Η εναλλαγή αυτή των δύο τιμών γίνεται με τη βοιηθητική μεταβλητή t (η οποία έχει δηλωθεί του ίδιου τύπου SINGLE όπως και οι αριθμοί που διατάσσουμε).

Η παρατήρηση κλειδί για τη μέθοδο bubble sort είναι ότι αν πρώτα ελέγξουμε την πρώτη θέση με όλες τις επόμενές της (δηλ. τις a(2), a(3), ... , a(n)), έπειτα τη δεύτερη θέση με όλες τις επόμενές της (δηλ. τις a(3), a(4), ... , a(n)), κλπ, έως ότου στο τέλος ελέγξουμε τις θέσεις a(n-1), a(n), τότε, στο τέλος αυτής της διαδικασίας ο πίνακας a είναι ταξινομημένος σε αύξουσα σειρά.

Αυτή η διπλή ανακύκλωση υλοποιείται στις γραμμές 15-23. Υπάρχει ένα εξωτερικό FOR loop στο οποίο ο δείκτης i διατρέχει τις τιμές 1, 2, ... , n. Στο εσωτερικό FOR loop, που ανοίγει στη γραμμή 16 και κλείνει με το NEXT της γραμμής 22, ο δείκτης j διατρέχει όλες τις τιμές μεγαλύτερες του i (έως το n). Το IF της γραμμής 17 υπάγεται στο εσωτερικό loop και άρα εκτελείται ακριβώς μια φορά για κάθε ζεύγος i, j με i<j.

Η διατεταγμένη σε αύξουσα σειρά λίστα τυπώνεται στις γραμμές 25-27. Το μόνο αξιοσημείωτο εδώ είναι το σύμβολο ; στο τέλος της εντολής PRINT. Το βάζουμε αυτό για να τυπωθούν όλα τα νούμερα στην ίδια γραμμή. Αν δεν το βάζαμε θα τυπώνονταν κάθε νούμερο στη γραμμή του.

Άσκηση 7. Τι αλλαγές πρέπει να κάνετε στο πρόγραμμα sort.bas ώστε να διατάσσει μια λίστα αριθμών σε αύξουσα σειρά του ημιτόνου της; Αν δηλ. $i<j$ τότε στην διατεταγμένη λίστα θα πρέπει να ισχύει $\sin(a_i) \le \sin(a_j)$. Η συνάρτηση $\sin(x)$ υπολογίζεται στην qbasic με τη συνάρτηση βιβλιοθήκης SIN(x).

2.5 Το πρόγραμμα integral.bas για προσεγγιστικό υπολογισμό του ολοκληρώματος μιας συνάρτησης



   1 DIM a, b, s, l AS SINGLE
   2 DIM N, i AS INTEGER
   3 
   4 CLS
   5 
   6 PRINT "Give me a, b, N:"
   7 INPUT a, b, N
   8 
   9 s = 0
  10 l = (b - a) / N
  11 
  12 FOR i = 0 TO N - 1
  13  t = a + i * l
  14  s = s + EXP(-t * t)
  15 NEXT
  16 
  17 s = s * l
  18 PRINT "Approximation to the integral is"; s
  19 

Το αρχείο integral.bas


Σε αυτό το πρόγραμμα υπολογίζουμε προσεγγιστικά την τιμή του ολοκληρώματος

\begin{displaymath}
\int_a^b e^{-x^2} dx,
\end{displaymath}

όπου τα όρια $a, b$ δίδονται από το χρήστη. Η συνάρτηση $e^{-x^2}$ επελέγη επειδή δεν μπορεί το ολοκλήρωμα αυτό να υπολογιστεί ακριβώς.
Η προσέγγιση του ολοκλρώματος γίνεται με τα λεγόμενα αθροίσματα Riemann, όπως φαίνεται στην εικόνα, όπου το εμβαδό κάτω από τη συνάρτηση $f$ προσεγγίζεται από το άθροισμα των εμβαδών των ορθογωνίων που φαίνονται, και τα οποία προκύπτουν ως εξής. Χωρίζουμε το διάστημα $[a,b]$ σε $N$ ίσα διαστήματα, το κάθε ένα από αυτά μήκους $\ell=(b-a)/N$. Τα διαχωριστικά (ή αλλιώς κομβικά) σημεία της διαμέρισης αυτής είναι τα

\begin{displaymath}
t_0=a, t_1 = a+\ell, t_2 = a+2\ell, \ldots, t_{N-1} = a+(N-1)\ell, t_N=b.
\end{displaymath}

Τα $N$ ορθογώνια που χρησιμοποιούμε είναι αυτά πού έχουν βάση τα διαστήματα $[t_i, t_{i+1}]$ (για $i=0,1,\ldots,N-1$) και ύψος το $f(t_i)$, άρα το συνολικό εμβαδό τους είναι το άθροισμα

\begin{displaymath}
\sum_{i=0}^{N-1} \ell f(t_i) = \frac{b-a}{N} \sum_{i=0}^{N-1} f(t_i).
\end{displaymath}

Στις γραμμές 1 και 2 του προγράμματος δηλώνουμε όλες τις μεταβλητές που πρόκειται να χρησιμοποιήσουμε και στις γραμμές 6 και 7 διαβάζουμε τις παραμέτρους του προβλήματος a, b, N (το διάστημα ολοκλήρωσης και σε πόσα διαστηματάκια θα το χωρίσουμε). Γενικά, όσο μεγαλύτερο είναι το N τόσο καλύτερη προσέγγιση θα πάρουμε και τόσο περισσότερο χρόνο παίρνει ο υπολογισμός της προσέγγισης αυτής.

Στη γραμμή 9 δίνουμε αρχική τιμή 0 στη μεταβλητή s που θα χρησιμοποιήσουμε για το τρέχον άθροισμα, και υπολογίζουμε στη μεταβλητή l το μήκος των μικρών διαστημάτων. Τέλος, το άθροισμα υπολογίζεται με ένα FOR loop στις γραμμές 8-15. Στη γραμμή 13 υπολογίζεται το αριστερό άκρο του διαστήματος και στη γραμμή 14 υπολογίζεται η συνάρτηση $e^{-x^2}$ σε αυτό το σημείο και προστίθεται η τιμή του στο τρέχον άθροισμα. (Η συνάρτηση EXP(x) είναι συνάρτηση βιβλιοθήκης της qbasic και υπολογίζει την εκθετική συνάρτηση $e^x$.) Στη γραμμή 17 πολλαπλασιάζεται το άθροισμα που υπολογίστηκε (το άθροισμα δηλ. όλων των υψών των ορθογωνίων) επί την κοινή του βάση.

Άσκηση 8. Τροποποιείστε το πρόγραμμα integral.bas ώστε να χρησιμοποιεί ως ύψος των ορθογωνίων όχι την τιμή της συνάρτησης στο αριστερό άκρο του διαστήματος $[t_i, t_{i+1}]$ αλλά την τιμή της συνάρτησης στο μέσο του διαστήματος αυτού.

3 Διάλεξη Τρίτη - 18 Δεκ. 2009

3.1 Το πρόγραμμα base.bas για μια απλή βάση δεδομένων

Το αρχείο base.bas βρίσκεται εδώ.

Στην τρίτη διάλεξη φτιάξαμε ένα σχετικά μεγάλο πρόγραμμα το οποίο διαχειρίζεται μια απλή βάση δεδομένων. Τα στοιχεία (εγγραφές, records, entries) στη βάση αυτή είναι ζεύγη (Όνομα, Τηλέφωνο) (ποιος έχει ποιο νούμερο).

Η βάση μας έχει ως μόνιμο αποθηκευτικό χώρο ένα αρχείο στο δίσκο του οποίου το όνομα περιγράφεται μέσα στο πρόγραμμά μας (δε μπορεί να το αλλάξει ο χρήστης του προγράμματος παρά μόνο ο προγραμματιστής) με την εντολή

filename = "d:\src\qbasic\database"

Η βάση πρέπει να διαβαστεί στη μνήμη για να γίνουν οποιεσδήποτε πράξεις σε αυτή (προσθήκες, διαγραφές, επιθεώρηση της βάσης). Ο χώρος που καταλαμβάνει η βάση μας στο δίσκο είναι οι τρεις μεταβλητές n, nm, phone που ορίζονται ως

' n is the number of entries in the database
' the variable changed is 1 if the database has been changed but not saved to disk
DIM SHARED n, changed AS INTEGER
' the array nm holds the names
DIM SHARED nm(100) AS STRING
' the arrray phone holds the telephone numbers.
' phone(1) is the phone number of person with name nm(1), etc
DIM SHARED phone(100) AS INTEGER

n είναι το πλήθος των εγγραφών (ζευγών) στη βάση μας. Τα ονόματα αποθηκεύονται στον πίνακα nm(100) και τα τηλέφωνα στον πίνακα phone(100) (100 είναι λοιπόν το μέγιστο μέγεθος που μπορεί να φτάσει η βάση μας).

3.1.1 Η μορφή του αρχείου

Η σύμβαση που ακολουθούμε είναι ότι η πρώτη γραμμή του αρχείου μας έχει μόνο ένα ακέραιο (μη αρνητικό) αριθμό ο οποίος μας λέει πόσες εγγραφές υπάρχουν στη βάση μας. Μετά την πρώτη γραμμή του αρχείου, κάθε εγγραφή καταλαμβάνει δύο γραμμές στο αρχείο, η πρώτη από τις οποίες περιέχει το όνομα και η δεύτερη το τηλέφωνο (που κάνουμε τη σύμβαση ότι είναι ακέραιος αριθμός, άρα δεν περιέχει κενά, παύλες κλπ).

Ένα παράδειγμα ενός τέτοιου αρχείου μπορείτε να δείτε εδώ:



   1  3 
   2 Mihalis
   3  3834 
   4 Maria
   5  3741 
   6 Yannis
   7  1234 

Το αρχείο database


3.1.2 Διάβασμα και γράψιμο αρχείων

Για την περίπτωση που μας ενδιαφέρει (αρχεία τύπου text) το διάβασμα και το γράψιμο ενός αρχείου γίνεται με τις εντολές input και print όπως και το i/o από το πρηκτρολόγιο, οι οποίες όμως παίρνουν μια έπιπλέον παράμετρο που προσδιορίζει από πού διαβάζουν ή πού γράφουν.

Πρέπει πρώτα απ' όλα να «ανοίξουμε» το αρχείο για διάβασμα

OPEN filename FOR INPUT AS #1
ή για γράψιμο
OPEN filename FOR OUTPUT AS #1
όπου έχουμε πριν θέσει για τη μεταβλητή filename τύπου STRING
filename = "d:\src\qbasic\database"
Η εντολή OPEN λοιπόν ανοίγει το αρχείο (του οποίο το όνομα έχουμε δώσει) για διάβασμα ή για γράψιμο, ως «ανοιχτό αρχείο» #1.

Έπειτα το διάβασμα ή γράψιμο από αυτό ή σε αυτό γίνονται με τις εντολές INPUT #1, ... ή PRINT #1, ....

3.1.3 Υποπρογράμματα (Subroutines)

Υποπρόγραμμα (subroutine, procedure, function, ανάλογα με τη γλώσσα προγραμματισμού) είναι το πακετάρισμα ενός κομματιού κώδικα που κάνει μια συγκεκριμένη δουλειά, με τρόπο ώστε αυτός ο κώδικας να μπορεί να χρησιμοποιείται σε διάφορα σημεία του προγράμματος χωρίς να επαναλαμβάνεται. Στη διάλεξη αυτή θα δούμε υποπρογράμματα της qbasic τύπου sub, και δε θα δούμε υποπρογράμματα με παραμέτρους (που είναι ένα πολύ ουσιαστικό κομμάτι της χρήσης των υποπρογραμμάτων).

Η δομή του προγράμματός μας είναι το κυρίως πρόγραμμα και μια συλλογή από υπορουτίνες, συγκεκριμένα οι υπορουτίνες

add, del, finish, infile, listdata, menu και outfile,
με αλφαβητική σειρά.

Ας δούμε το παράδειγμα της υπορουτίνας (sub) με το όνομα listdata η οποία σκοπό έχει, όποτε καλείται, να τυπώνει τα περιεχόμενα της βάσης δεδομένων που φτιάχνουμε:



   1 ' Display the contents of the database
   2 SUB listdata
   3 
   4 DIM i AS INTEGER
   5 
   6 PRINT "--------------------------------------"
   7 FOR i = 1 TO n
   8  PRINT i, nm(i); " Tel: "; phone(i)
   9 NEXT
  10 PRINT "--------------------------------------"
  11 
  12 END SUB

Το αρχείο listdata.sub


Ο κώδικας που αντιστοιχεί στην υπορουτίνα γράφεται ανάμεσα στις γραμμές 2 και 12. Παρατηρείστε ότι οι μεταβλητές n, nm και phone είναι οι ίδιες οι μεταβλητές που έχουν δηλωθεί στο κυρίως πρόγραμμα ως SHARED. Η μεταβλητή i που δηλώνεται μέσα στη SUB listdata είναι αυτό που ονομάζουμε «τοπική μεταβλητή» και είναι ορατή μόνο μέσα στη SUB listdata. Αν κάπου αλλού στον κώδικά μας (μέσα σε μια άλλη υπορουτίνα ή μέσα στο κυρίως πρόγραμμα) υπάρχει μια μεταβλητή i αυτή αντιστοιχεί σε άλλη θέση μνήμης (σε άλλο «κουτί») από την i της SUB listdata.

Αν θέλουμε τώρα να «καλέσουμε» την υπορουτίνα listdata τότε δεν έχουμε παρά να δώσουμε την εντολή CALL listdata στο σημείο όπου θέλουμε να τυπωθεί στην οθόνη η λίστα με τα ονόματα και τα τηλέφωνα.

Δείτε την υπορουτίνα menu η οποία χειρίζεται την αλληλεπίδραση με το χρήστη:



   1 ' This subroutine interacts with the user
   2 SUB menu
   3 
   4 DIM choice AS INTEGER
   5 
   6 ' display the user's options
   7 menutop:
   8 
   9 PRINT
  10 PRINT "My first database program"
  11 PRINT
  12 PRINT "1. Read database from disk"
  13 PRINT "2. Write database to disk"
  14 PRINT "3. List the database"
  15 PRINT "4. Add to the database"
  16 PRINT "5. Delete from the database"
  17 PRINT "0. Quit"
  18 
  19 PRINT
  20 INPUT "Your choice:", choice
  21 
  22 SELECT CASE choice
  23 CASE 1
  24  CALL infile
  25 CASE 2
  26  CALL outfile
  27 CASE 3
  28  CALL listdata
  29 CASE 4
  30  CALL add
  31 CASE 5
  32  CALL del
  33 CASE 0
  34  CALL finish
  35 CASE ELSE
  36  PRINT "Choose again"
  37  GOTO menutop
  38 END SELECT
  39 
  40 GOTO menutop
  41 
  42 END SUB

Το αρχείο menu.sub


Η ρουτίνα αυτή τυπώνει μια λίστα με δυνατές επιλογές του χρήστη (γραμμές 9-17) και ρωτάει το χρήστη για το τι θέλει να κάνει (μεταβλητή choice, γραμμές 19-20). Στις γραμμές 22-38, με τη σύνθετη εντολή

SELECT CASE choice ... CASE xxx ... CASE yyy ... CASE ELSE ... END SELECT
εκτελείται ένα διαφορετικό κομμάτι κώδικα ανάλογα με την τιμή της μεταβλητής choice. Σε κάθε επιλογή (εκτός από το CASE ELSE ...) εκτελείται μια κατάλληλη υπορουτίνα η οποία χειρίζεται τη συγκεκριμένη δουλειά που θέλει να κάνει ο χρήστης. Σε περίπτωση που ο χρήστης δεν επέλεξε μια από τις επιλογές που του προσφέρθηκαν (γραμμές 35-37) αλλά και μετά από κάθε επιτυχή διεκπεραίωση της επιθυμίας του χρήστη το μενού (γραμμή 7) ξανατυπώνεται στην οθόνη και ο χρήστης καλείται να επιλέξει ξανά.

Φανταστείτε πόσο πιο δυσανάγνωστος θα ήταν ο κώδικας της ρουτίνας menu αν αντί για την κλήση μιας υπορουτίνας σε κάθε CASE είχαμε ολόκληρο τον κώδικα της κάθε υπορουτίνας στην αντίστοιχη θέση.

Η υπορουτίνα menu είναι η μόνη που καλείται από το κυρίως πρόγραμμα:



   1 ' declarations of subroutines
   2 DECLARE SUB finish ()
   3 DECLARE SUB del ()
   4 DECLARE SUB add ()
   5 DECLARE SUB outfile ()
   6 DECLARE SUB menu ()
   7 DECLARE SUB infile ()
   8 DECLARE SUB listdata ()
   9 
  10 ' start of the main program
  11 
  12 DIM i AS INTEGER
  13 
  14 ' variables declared as SHARED are visible inside the subroutines
  15 
  16 ' this is the file holding the database
  17 DIM SHARED filename AS STRING
  18 ' n is the number of entries in the database
  19 ' the variable changed is 1 if the database has been changed but not saved to disk
  20 DIM SHARED n, changed AS INTEGER
  21 ' the array nm holds the names
  22 DIM SHARED nm(100) AS STRING
  23 ' the arrray phone holds the telephone numbers.
  24 ' phone(1) is the phone number of person with name nm(1), etc
  25 DIM SHARED phone(100) AS INTEGER
  26 
  27 ' You may have to change this to run this
  28 ' program on your system
  29 filename = "d:\src\qbasic\database"
  30 
  31 ' initially the database is empty
  32 n = 0
  33 changed = 0
  34 
  35 ' clear the screen
  36 CLS
  37 
  38 ' call subroutine menu which handles the interaction with the user
  39 CALL menu
  40 
  41 END
  42 
  43 ' end of main program
  44 

Το αρχείο main.sub


Οι γραμμές 2-8 του κυριως προγράμματος έχουν μπεί στη θέση αυτή αυτόματα από την qbasic αφότου γράψαμε την κάθε υπορουτίνα. Στην qbasic για να προσθέσουμε μια υπορουτίνα επιλέγουμε Edit -> New Sub από τα μενού στην πάνω γραμμή της οθόνης. Το αρχείο του προγράμματός μας όμως αποθηκεύεται από την qbasic ως ένα ενιαίο αρχείο (δείτε το εδώ).

Η SHARED μεταβλητή filename που δηλώνεται στη γραμμή 17 και παίρνει τιμή 29 είναι το όνομα του αρχείου στο δίσκο όπου αποθηκεύεται η βάση μας μετά το πέρας του προγράμματος (κατά τη διάρκεια ζωής του οποίου η βάση μας βρίσκεται αποθηκευμένη στους πίνακες nm, phone οι οποίοι είναι στην κεντρική μνήμη (RAM) του υπολογιστή).

Στη γραμμή 32 η μεταβλητή n που αντιπροσωπεύει το πλήθος των εγγραφών στη βάση μας γίνεται 0 (κενή βάση) και η μεταβλητή changed γίνεται επίσης ίση με 0. Το νόημα της μεταβλητής changed είναι ότι θέλουμε να είναι 1 αν και μόνο αν η βάση μας έχει «πειραχτεί» (έχουμε αλλάξει κάτι σε αυτή, π.χ. σβήνοντας ή προσθέτοντας εγγραφές) και δεν έχει ακόμη αποθηκευτεί στο δίσκο. Αυτό μας βοηθάει να προλάβουμε μια απροσεξία του χρήστη ο οποίος μπορεί να φύγει από το πρόγραμμα (επιλογή 0 στο μενού) χωρίς να έχει αποθηκεύσει τη βάση στο δίσκο (έτσι οι αλλαγές του θα χαθούν).

Πέρα από το να καθαρίσει την οθόνη το μόνο πράγμα που κάνει το κυρίως πρόγραμμα είναι να καλέσει την υπορουτίνα menu η οποία θα αλληλεπιδρά με το χρήστη.

Δείτε την υπορουτίνα finish που χειρίζεται το τελείωμα του προγράμματος:



   1 ' This subroutine handles exiting the program
   2 SUB finish
   3 
   4 DIM ans AS INTEGER
   5 
   6 ' if the database is unsaved, doublecheck the user's intentions
   7 IF changed = 1 THEN
   8  PRINT "The database has been changed."
   9  INPUT "Do you still want to quit (1 for yes, 0 for no)? ", ans
  10  IF ans <> 1 THEN
  11   EXIT SUB
  12  END IF
  13 END IF
  14 
  15 PRINT "Bye bye"
  16 END
  17 
  18 END SUB

Το αρχείο finish.sub


Στις γραμμές 7-13 ελέγχει αν πάμε να φύγουμε χωρίς να έχουμε «σώσει» τη βάση στο δίσκο (changed=1). Σε αυτή την περίπτωση ο χρήστης ερωτάται ξανά και, αν επιμένει (ans=1) τότε μόνο φεύγουμε από το πρόγραμμα (γραμμές 15-16) αλλιώς η υπορουτίνα επιστρέφει (EXIT SUB στη γραμμή 11) τον έλεγχο στην υπορουτίνα menu από όπου και κλήθηκε.

Παρατηρείστε επίσης ότι στη γραμμή 9 χρησιμοποιούμε μια νέα μορφή της εντολής INPUT, στην οποία η πρώτη παράμετρος είναι ένα κείμενο που τυπώνεται στο χρήστη και η δεύτερη είναι η μεταβλητή που διαβάζουμε. Στη θέση της γραμμής 9 δηλ. θα μπορούσαμε να είχαμε γράψει τις δύο γραμμές

PRINT "Do you still want to quit (1 for yes, 0 for no)? ";
INPUT ans
με το ίδιο αποτέλεσμα.

Οι υπορουτίνες infile και outfile που διαβάζουν από το δίσκο τη βάση και τη γράφουν στο δίσκο φαίνονται εδώ:



   1 ' Read the database from the disk, into the arrays nm(100) and phone(100)
   2 SUB infile
   3 
   4 DIM i AS INTEGER
   5 
   6 ' open the file holding the database for reading
   7 OPEN filename FOR INPUT AS #1
   8 
   9 ' read the database length from the first line of the file
  10 INPUT #1, n
  11 ' the read the names and phone numbers, one line per item
  12 FOR i = 1 TO n
  13  INPUT #1, nm(i)
  14  INPUT #1, phone(i)
  15 NEXT
  16 ' close the file
  17 CLOSE #1
  18 
  19 PRINT "The database file has been read"
  20 
  21 END SUB

Το αρχείο infile.sub




   1 ' This subroutine writes the database (arrrays nm and phone) to the disk
   2 SUB outfile
   3 
   4 DIM ans, i AS INTEGER
   5 
   6 ' if the database to be saved to disk is empty, doublecheck with the user
   7 IF n = 0 THEN
   8  PRINT "Database is empty."
   9  INPUT "Are you sure (1 for yes, 0 for no)? ", ans
  10  IF ans <> 1 THEN
  11   PRINT "Save operation cancelled"
  12   EXIT SUB
  13  END IF
  14 END IF
  15 
  16 ' open the file to write
  17 OPEN filename FOR OUTPUT AS #1
  18 
  19 ' write the data to the file in a way that makes it possible to read them back
  20 ' with subroutine infile
  21 PRINT #1, n
  22 FOR i = 1 TO n
  23  PRINT #1, nm(i)
  24  PRINT #1, phone(i)
  25 NEXT
  26 
  27 ' close the file
  28 CLOSE #1
  29 
  30 ' mark the database as unchanged (saved)
  31 changed = 0
  32 
  33 PRINT "Database saved to file"
  34 
  35 END SUB

Το αρχείο outfile.sub


Το αρχείο ανοίγεται ως #1 και κλείνεται στο τέλος (γραμμές 7 και 17 στην infile και γραμμές 17 και 28 στην outfile). Αφού η infile διαβάσει την πρώτη γραμμή του αρχείου (γραμμή 10) ξέρει μετά πόσες ακόμη γραμμές έχει να διαβάσει και έτσι γεμίζει τους πίνακες nm και phone (γραμμές 12-15) πριν κλείσει το αρχείο.

Στις γραμμές 7-11 της outfile χειρζόμαστε ξεχωριστά την περίπτωση όπου ο χρήστης πάει να γράψει μια κενή βάση στο δίσκο (n=0). Επειδή είναι πολύ πιθανό να πρόκειται για λάθος (φανταστείτε πόσο εύκολο είναι ο χρήστης να τρέξει το πρόγραμμα και η πρώτη εντολή που θα δώσει να είναι η 2 που σημαίνει ότι θα πάει να γράψει μια κενή βάση στο δίσκο καταστρέφοντας την παλιά) ξαναρωτάμε το χρήστη και προχωράμε μόνο αν μας το επιβεβαιώσει (ans=1) αλλιώς φεύγουμε από την outfile (γραμμή 12).

Στη γραμμή 31 η μεταβλητή changed γίνεται 0, δείχνοντάς μας ότι τα περιεχόμενα της βάσης στο δίσκο αντιστοιχούν σε αυτά της βάσης στη μνήμη (πίνακες nm και phone).

Ακολουθεί η ρουτίνα add για την προσθήκη μιας εγγραφής στη βάση (στο τέλος των πινάκων):



   1 ' The following subroutine adds a person to the database
   2 SUB add
   3 
   4 IF n = 100 THEN
   5  PRINT "Database if full"
   6  EXIT SUB
   7 END IF
   8 
   9 ' the person is added at the end of the list
  10 INPUT "Give name: ", nm(n + 1)
  11 INPUT "Give phone number: ", phone(n + 1)
  12 
  13 ' increment the length by one and mark the database as changed (unsaved)
  14 n = n + 1
  15 changed = 1
  16 
  17 END SUB

Το αρχείο add.sub


Στις γραμμές 4-7 ελέγχουμε αν η βάση μας έχει το μέγιστο αριθμό εγγραφών (100) που έχουμε δεσμεύσει όταν δηλώσαμε τους πίνακές μας, και αν αυτό ισχύει τότε η ρουτίνα επιστρέφει (γραμμή 6) χωρίς να προσθέσει καμία εγγραφή. Η προσθήκη γίνεται στις γραμμές 10 και 11 και στη γραμμή 14 το n αυξάνεται κατά 1 ώστε η νέα μας εγγραφή να φαίνεται. Στη γραμμή 15 κάνουμε 1 το changed ώστε να φαίνεται ότι η βάση έχει αλλαχτεί αλλά όχι ακόμη εγγραφεί στο δίσκο.

Η διαγραφή μιας εγγραφής από τη βάση γίνεται από την ρουτίνα del:



   1 ' The following subroutine deletes a person from the database
   2 SUB del
   3 
   4 DIM i, ans AS INTEGER
   5 
   6 ' call the subroutine listdata which lists the database for the user to choose whom to delete
   7 CALL listdata
   8 
   9 INPUT "Which one to delete? ", ans
  10 
  11 ' check if out of range
  12 ' this also allows the user to change his mind
  13 IF ans <= 0 OR ans > n THEN
  14  EXIT SUB
  15 END IF
  16 
  17 ' shift one place up all persons following the one deleted
  18 FOR i = ans + 1 TO n
  19  nm(i - 1) = nm(i)
  20  phone(i - 1) = phone(i)
  21 NEXT
  22 
  23 ' reduce n by 1, mark the database as changed (unsaved)
  24 n = n - 1
  25 changed = 1
  26 
  27 PRINT "Element No "; ans; " deleted"
  28 
  29 END SUB

Το αρχείο del.sub


Στη γραμμή 7 καλείται η ρουτίνα listdata ώστε να υπενθυμίσουμε στο χρήστη το ποιες είναι οι εγγραφές και να επιλέξει ποια εγγραφή θέλει να σβήσει. Εδώ φαίνεται ξεκάθαρα το πλεονέκτημα που έχει το να κάνουμε την listdata υπορουτίνα. Αν δεν το είχαμε κάνει και είχαμε απλά γράψει τον κώδικά της στο αντίστοιχο CASE της ρουτίνας menu τότε θε έπρεπε να το ξαναγράψουμε εδώ.

Στη γραμμή 9 ο χρήστης ερωτάται ποια εγγραφή θέλει να σβήσει και αν η απάντηση του χρήστη είναι εκτός των ορίων 1 .. n (γραμμές 13-14) τότε η υπορουτίνα επιστρέφει (αυτός είναι και ένας τρόπος να επιτρέψουμε στο χρήστη να αλλάξει γνώμη και να μη σβήσει τίποτα από τη βάση).

Για να σβήσουμε την εγγραφή ans πρέπει οι εγγραφές που την ακολουθούν στους πίνακες να ανέβουν μια θέση πρς τα πάνω. Πρέπει δηλ. η εγγραφή ans+1 να πάει στη θέση της ans, έπειτα η εγγραφή ans+2 να πάει στη θέση της ans+1, και τέλος η εγγραφή n να πάει στη θέση n-1 (γραμμές 18-21) και το n να μειωθεί κατά 1 (γραμμή 24). Τέλος (γραμμή 25) μαρκάρουμε τη βάση ως αλλαγμένη και μη αποθηκευμένη στο δίσκο.

Άσκηση 9. Στο πρόγραμμα base.bas η οθόνη καθαρίζεται μόνο μια φορά, στην αρχή του κυρίως προγράμματος. Αυτό κάνει τα μηνύματα του προγράμματος κάπως δυσανάγνωστα. Μια λύση θα ήταν να συμπεριλάβουμε την εντολή CLS ακριβώς μετά την ετικέτα menutop: στη ρουτίνα menu. Όμως αυτό θα καθάριζε την οθόνη αμέσως μετά τα όποια μηνύματα του προγράμματος προς το χρήστη με αποτέλεσμα ο χρήστης να μη προλαβαίνει να τα διαβάζει. Πώς μπορεί να διορθωθεί αυτή η προσέγγιση, ώστε και ο χρήστης να έχει όσο χρόνο χρειάζεται για να διαβάσει τα μηνύματα και η οθόνη να καθαρίζει πριν από κάθε τύπωμα του μενού εντολών;

Ένα παρόμοιο πρόβλημα υπάρχει με τη ρουτίνα listdata. Αν η βάση έχει πολλά άτομα τότε, επειδή ο οθόνη του υπολογιστή έχει λίγες γραμμές (π.χ. 25), αν τα τυπώσουμε όλα δεν πρόκειται να δούμε τα πρώτα μια και θα κυλήσουν προς την πάνω μεριά της οθόνης και θα αντικατασταθούν από τα μετέπειτά τους. Πώς μπορούμε να το λύσουμε αυτό το πρόβλημα ώστε να τυπώνουμε κάθε φορά μόνο όσα ονόματα χωράνε στην οθόνη;

Άσκηση 10. Αλλάξτε το πρόγραμμα ώστε να μπορεί ο χρήστης να καθορίζει το όνομα του αρχείου που κρατάμε τη βάση. Για παράδειγμα, θα μπορούσατε να προσθέσετε μια νέα εντολή στο μενού που να επιτρέπει στο χρήστη να κάνει ακριβώς αυτό. Επίσης, αν ο χρήστης δεν το κάνει και, παρ'όλ' αυτά, επιχειρήσει να διαβάσει τη βάση από το δίσκο, τότε το πρόγραμμα να του βγάζει ένα διευκρινιστικό μήνυμα ότι πρέπει πρώτα να καθορίσει τη βάση και μετά να πάει να τη διαβάσει.

Επίσης προσθέστε και μια εντολή τύπου "Save As", που επιτρέπει δηλ. στο χρήστη να σώσει τη βάση σε άλλο αρχείο, το οποίο αρχείο γίνεται από εκείνη τη στιγμή και πέρα, το τρέχον αρχείο.

Τέλος προσθέστε και μια εντολή τύπου "Close", που επιτρέπει στο χρήστη να μηδενίσει τη βάση που υπάρχει στη μνήμη ώστε ενδεχομένως αργότερα να ανοίξει μια άλλη (δίνοντας άλλο όνομα αρχείου). Η ρουτίνα αυτή θα πρέπει να εξετάζει αν έχει σωθεί ήδη στο δίσκο η υπάρχουσα βάση και, αν όχι, να δίνει την ευκαιρία στο χρήστη να το κάνει πριν την «κλείσει».



Mihalis Kolountzakis 2011-06-01