Γλώσσες προγραμματισμού
Διομήδης Σπινέλλης
Τμήμα Διοικητικής Επιστήμης και Τεχνολογίας
Οικονομικό Πανεπιστήμιο Αθηνών
dds@aueb.gr
Εισαγωγή - Η γλώσσα C++
Καλώς ήρθατε
Γλώσσες προγραμματισμού
Τι περιλαμβάνει το μάθημα
- Εισαγωγή - Η γλώσσα C++
- Κλάσεις και αντικείμενα
- Καθοριζόμενοι τελεστές
- Κληρονομικότητα
- Αντικειμενοστρεφής σχεδιασμός με UML
- Πρότυπα στη C++
- Υλοποίηση με έτοιμες βιβλιοθήκες
- Υλοποίηση διεπαφών σε Visual Basic
- Χειρισμός δεδομένων με SQL
- Συναρτησιακός προγραμματισμός στη Lisp
- Κατηγορήματα στην Prolog
- Ταυτοχρονισμός στα Windows NT
- Ολοκληρωμένα περιβάλλοντα ανάπτυξης
- Ανασκόπηση - επανάληψη
Οι σημειώσεις
Εισαγωγή
- Η C++ είναι μια γλώσσα που έχει προέλθει από τη C.
- Πλεονεκτήματά της σε σχέση με τη C είναι:
- Τα παραπάνω στοιχεία επιτρέπουν στη γλώσσα να υποστηρίζει τον
αντικειμενοστρεφή προγραμματισμό (object-oriented programming).
- Υπάρχουν παρόλ' αυτά εφαρμογές οι οποίες γράφονται καλύτερα σε C.
Τέτοιες είναι:
- προγράμματα που έχουν άμεση σχέση με το υλικό όπως ο
πυρήνας του λειτουργικού συστήματος και οι οδηγοί συσκευών,
- ενσωματωμένες (embedded) εφαρμογές που εκτελούνται
σε περιβάλλον που δεν μπορεί να ανταποκριθεί στις απαιτήσεις της C++.
- Άλλες γλώσσες στην ίδια οικογένεια με τη C++ (1983) είναι:
- η Simula67 (1967),
- η Smalltalk (1979) και
- η Java (1995).
Το πρώτο πρόγραμμα σε C++
Το πρώτο πρόγραμμα σε C++ θα τυπώσει,
όπως θα περίμενε κανείς, τις λέξεις hello, world:
#include <iostream.h>
main()
{
cout << "hello, world\n";
}
Μεταγλώττιση
Στο περιβάλλον του εργαστηρίου (Windows NT, Visual C++)
το πρόγραμμα πρέπει να έχει επίθεμα
.cpp αντί για .c για να το αναγνωρίσει ο μεταγλωττιστής ως πρόγραμμα
C++.
Ο μεταγλωττιστής παραμένει ο ίδιος (cl).
Σε εγκαταστάσεις Linux το πρόγραμμα πρέπει να έχει επίθεμα .C.
Για να μεταγλωττιστεί χρησιμοποιούμε την εντολή g++.
C++ ως καλύτερη C
Η C++ προσφέρει πρόσθετες δυνατότητες σε σχέση με τη C:
- Ορισμοί μεταβλητών μπορούν να δοθούν σε οποιοδήποτε σημείο
μπορεί να δοθεί μια έκφραση.
Παράδειγμα:
#include <iostream.h>
main()
{
int i = 3;
cout << i;
for (int j = 0; j < 10; j++)
cout << j;
}
Καλό είναι οι μεταβλητές να ορίζονται ακριβώς πριν από το πρώτο σημείο όπου
χρησιμοποιούνται.
- Σχόλια της μιας γραμμής μπορούν να οριστούν με τους χαρακτήρες //:
Παράδειγμα:
int i;
// This is a line comment; set i to 8.
i = 8;
- Μεταβλητές μπορούν να οριστούν ως αναφορά (reference)
σε μια άλλη.
Παράδειγμα:
int y = 8;
int &x = y;
x = 3; // Set y to 3
- Συναρτήσεις μπορούν να οριστούν πολυμορφικά με βάση τον τύπο του
ορίσματός τους.
Παράδειγμα:
#include <iostream.h>
double square(double x);
int square(int x);
double
square(double x)
{
return (x * x);
}
int
square(int x)
{
return (x * x);
}
main()
{
int i = 3;
double d = 1.4142;
cout << "square(" << i << ")=" << square(i) << "\n";
cout << "square(" << d << ")=" << square(d) << "\n";
}
- Οι δηλώσεις δομών ορίζουν αυτόματα και έναν νέο τύπο με αντίστοιχο
όνομα. Ο τελεστής new επιστρέφει μνήμη με το σωστό μέγεθος και τύπο για
δομές και κλάσεις.
Η μνήμη που δεσμεύει ο τελεστής new ελευθερώνεται με τον τελεστή delete.
Παράδειγμα:
struct point {
int x, y;
};
point *p;
main()
{
p = new point;
delete p;
}
- Οι τελεστές new και delete[] δεσμεύουν και ελευθερώνουν και μνήμη για
πίνακες.
Παράδειγμα:
main()
{
int *ip = new int[10];
ip[3] = 8;
delete[] ip;
}
- Στα ορίσματα των συναρτήσεων μπορούν να δωθούν
εξ' ορισμού (default) αρχικές τιμές.
Κατά την κλήση της αντίστοιχης συνάρτησης, το αντίστοιχο όρισμα
μπορεί να παραληφθεί.
Στην περίπτωση αυτή η μεταβλητή του ορίσματος μέσα στη συνάρτηση έχει την
αρχική τιμή που είχε δωθεί κατά τον ορισμό.
Παράδειγμα:
#include <iostream.h>
#include <math.h>
double
sqr(double x)
{
return (x * x);
}
/*
* Return the distance between two points, or the distance from (0,0)
*/
double
distance(double x0, double y0, double x1 = 0.0, double y1 = 0.0)
{
return (sqrt(sqr(x0 - x1) + sqr(y0 - y1)));
}
main()
{
cout << distance(4, 3) << "\n"; // Print 5
cout << distance(6, 6, 5, 5) << "\n"; // Print 1.4142...
}
- Ορίζεται ο τύπος
bool
για λογικές μεταβλητές που μπορούν να
λάβουν τιμές
true
και
false
.
Όλοι οι τελεστές που έχουν αποτέλεσμα μια λογική τιμή
παράγουν στη C++
αποτέλεσμα τύπου bool.
Οι τελεστές αυτοί είναι οι παρακάτω:
<
<=
>
>=
==
!=
&&
||
!
Είσοδος και έξοδος
Βιβλιογραφία
- Ellis Horowitz
Βασικές αρχές γλωσσών προγραμματισμού.
Δεύτερη αμερικάνικη έκδοση.
σ. 9-59, 235-238, 272-278, 421-447 Εκδόσεις Κλειδάριθμος, 1984.
- Brad J. Cox.
Object
Oriented Programming: An Evolutionary Approach.
Addison-Wesley, 1986.
- O-J. Dahl, B. Myrhaug, and
K. Nygaard.
SIMULA common base language.
Technical Report S-22, Norwegian Computing Center, Oslo, Norway, 1970.
- Erich Gamma, Richard
Helm, Ralph Johnson, and John Vlissides.
Design
Patterns: Elements of Reusable Object-Oriented Software.
Addison-Wesley, 1995.
- Adele Goldberg.
Smalltalk 80: The Language and its Implementation.
Addison Wesley, 1980.
- Dennis Kafura.
Object-Oriented Software Design and Construction with C++.
Prentice-Hall, 1998.
- Andrew Koenig and Barbara
Moo.
Ruminations on C++: A Decade of Programming Insight and
Experience.
Addison-Wesley, 1996.
- Glenn Krasner.
Smalltalk 80: Bits of History, Words of Advice.
Addison Wesley, 1980.
- Bertrand Meyer.
Object-oriented Software Construction.
Prentice Hall, 1988.
- Lewis J. Pinson and
Richard S. Wiener.
An
Introduction to Object-Oriented Programming and Smalltalk.
Addison-Wesley, 1988.
- James Rumbaugh, Michael
Blaha, William Premerlani, Frederick Eddy, and William Lorensen.
Object-Oriented Modeling and Design.
Prentice-Hall, 1991.
- Peter H. Salus, editor.
Handbook of Programming Languages, volume I: Object-Oriented Programming
Languages.
Macmillan Technical Publishing, 1998.
- Bjarne Stroustrup.
The
C++ Programming Language.
Addison-Wesley, third edition, 1997.
Πηγές στο Internet
Γενική βιβλιογραφία
- Harold Abelson,
Gerald Jay Sussman, and Jullie Sussman.
Structure and Interpretation of Computer Programs.
MIT Press, 1990.
- Grady Booch, James
Rumbaugh, and Ivar Jacobson.
The
Unified Modeling Language User Guide.
Addison-Wesley, 1999.
- James O. Coplien and
Douglas C. Schmidt.
Pattern Languages of Program Design.
Addison-Wesley, 1995.
- Margaret A. Ellis
and Bjarne Stroustrup.
The
Annotated C++ Reference Manual.
Addison-Wesley, 1990.
- Anthony J. Field and
Peter G. Harrison.
Functional Programming.
Addison-Wesley, 1988.
- Richard A. O'Keefe.
The
Art of Prolog.
MIT Press, 1990.
- James Rumbaugh, Michael
Blaha, William Premerlani, Frederick Eddy, and William Lorensen.
Object-Oriented Modeling and Design.
Prentice-Hall, 1991.
- Peter H. Salus, editor.
Handbook of Programming Languages, volume I: Object-Oriented Programming
Languages.
Macmillan Technical Publishing, 1998.
- Peter H. Salus, editor.
Handbook of Programming Languages, volume II: Imperative Programming
Languages.
Macmillan Technical Publishing, 1998.
- Peter H. Salus, editor.
Handbook of Programming Languages, volume III: Little Languages and
Tools.
Macmillan Technical Publishing, 1998.
- Peter H. Salus, editor.
Handbook of Programming Languages, volume IV: Functional and Logic
Programming Languages.
Macmillan Technical Publishing, 1998.
- Michael L. Scott.
Programming Language Pragmatics.
Morgan Kaufmann Publishers, 1999.
- Ravi Sethi.
Programming Languages: Concepts and Constructs.
Addison-Wesley, 1989.
- Bjarne Stroustrup.
The
C++ Programming Language.
Addison-Wesley, third edition, 1997.
Ασκήσεις
Άσκηση 1 (προαιρετική)
- Να μεταγράψετε σε C++ και να μεταγλωττίσετε
ένα από τα προγράμματα που έχετε γράψει σε C.
Επιλέξτε το πρόγραμμα έτσι ώστε να χρησιμοποιήσετε όσο περισσότερες
βελτιώσεις της C++ γίνεται.
Κλάσεις και αντικείμενα
Ορισμός κλάσεων
- Η δήλωση μιας κλάσης (class)
είναι παρόμοια με αυτή μιας δομής:
class point {
int x, y;
};
- Οι κλάσεις χρησιμοποιούνται για την υλοποίηση αφηρημένων τύπων
δεδομένων.
Για το σκοπό αυτό μπορούμε να ορίσουμε μέλη (members)
της κλάσης:
μεταβλητές και συναρτήσεις
που είναι ορατά μόνο από συναρτήσεις που αναφέρονται στον τύπο αυτό
(private) καθώς και μεταβλητές και συναρτήσεις που είναι καθολικά
ορατά (public).
Οι μεταβλητές ορίζουν ιδιότητες (properties)
και οι συναρτήσεις ορίζουν
μεθόδους πρόσβασης (methods)
των αντικειμένων της κλάσης.
Παράδειγμα:
class point {
private:
int x, y;
public:
int getx();
int gety();
void setpos(int sx, int sy);
void display();
};
Στο παραπάνω παράδειγμα τα μέλη (ιδιότητες) της κλάσης x, y δεν είναι ορατά και προσβάσιμα
παρά μόνο από τις συναρτήσεις (μεθόδους) της κλάσης getx, gety, setpos και display.
- Ο ορισμός των συναρτήσεων της κλάσης γίνεται με τη χρήση της
σύνταξης κλάση::συνάρτηση.
Παράδειγμα:
int
point::getx()
{
return (x);
}
int
point::gety()
{
return (y);
}
void
point::display()
{
cout << "(" << x << "," << y << ")\n";
}
void
point::setpos(int sx, int sy)
{
x = sx;
y = sy;
}
- Αφού δηλωθεί μια κλάση (τυπικά σε ένα αρχείο κλάση.h) και οριστούν
οι συναρτήσεις της (τυπικά σε ένα αρχείο κλάση.cpp) μπορούν να οριστούν
μεταβλητές με τον τύπο της κλάσης.
Πρόσβαση στις συναρτήσεις της κλάσης έχουν οι μεταβλητές αυτές με τη
σύνταξη μεταβλητή.συνάρτηση.
Μπορούμε επίσης να δηλώσουμε μεταβλητές δείκτες μιας κλάσης.
Πρόσβαση στις συναρτήσεις της κλάσης έχουν οι μεταβλητές αυτές με τη
σύνταξη μεταβλητή->συνάρτηση.
Παράδειγμα:
#include <iostream.h>
#include "point.h"
main()
{
point a;
point b, *c;
c = new point;
b.setpos(6, 6);
cout << b.getx();
a.display();
b.display();
c->display();
}
- Οι συναρτήσεις της κλάσης μπορούν να έχουν πρόσβαση στα στοιχεία της
κλάσης με τους εξής τρόπους:
- με το όνομά τους (π.χ. x),
- μέσω της ορισμένης από τη γλώσσα
μεταβλητής this που δείχνει στην κλάση (π.χ. this->x),
- με πρόθεμα το όνομα της κλάσης για να αποφεύγεται η σύγχυση
όταν υπάρχει τοπική μεταβλητή με το ίδιο όνομα (π.χ. point::x).
Παράδειγμα:
void
point::setpos(int sx, int sy)
{
this->x = sx;
point::y = sy;
}
Μέθοδοι κατασκευής και καταστροφής
- Σε κάθε κλάση μπορεί να οριστεί μια
μέθοδος κατασκευής (constructor) με όνομα ίδιο
με αυτό της κλάσης και μια
μέθοδος καταστροφής (destructor) με όνομα το
όνομα της κλάσης με το πρόθεμα ~.
-
Η μέθοδος κατασκευής καλείται κάθε φορά που δημιουργείται
ένα νέο αντικείμενο (σε επίπεδο καθολικό, τοπικό, ή με new).
-
Η μέθοδος καταστροφής καλείται κάθε φορά που παύει να υπάρχει
ένα αντικείμενο δηλαδή αντίστοιχα όταν τελειώνει το πρόγραμμα, όταν
η ροή βγαίνει από το τοπικό τμήμα, ή καλείται η delete.
-
Το όρισμα που δηλώνουμε στη μέθοδο κατασκευής επιτρέπει προσδιορισμό
ιδιοτήτων του αντικειμένου που δημιουργούμε (π.χ. τον αριθμό στοιχείων
σε μια στοίβα) ή αρχικών τιμών.
Για το ίδιο αντικείμενο μπορούμε να ορίσουμε πολλές μεθόδους κατασκευής
με διαφορετικά ορίσματα.
-
Η μέθοδος καταστροφής δε δέχεται κάποιο όρισμα.
-
Οι πίνακες δεν μπορούν να αρχικοποιηθούν με τη χρήση μεθόδων που
απαιτούν κάποιο όρισμα.
Έτσι, για αντικείμενα που απαρτίζουν πίνακες πρέπει να έχει δηλωθεί μια
εξ' ορισμού μέθοδος κατασκευής (default constructor)
η οποία ή δε δέχεται κανένα όρισμα ή όλα της τα ορίσματα έχουν δηλωμένες
εξ' ορισμού αρχικές τιμές.
-
Οι μέθοδοι αυτές μπορούν να χρησιμοποιηθούν για να διαχειριστούν τη
μνήμη αντικειμένων που απαιτούν τη χρήση δυναμικής μνήμης με κατάλληλες
κλήσεις στις new και delete.
Παράδειγμα:
#include <stdlib.h>
#include <iostream.h>
// Points in an N dimensional space
class pointnd {
private:
int *values;
int dimensions;
public:
pointnd(); // Default constructor
pointnd(int dimensions); // Constructor
pointnd(int dimensions, int value); // Constructor with initial value
~pointnd(); // Destructor
int &val(int dimension);
void display();
};
pointnd::pointnd()
{
pointnd::dimensions = 3;
values = new int[dimensions];
}
pointnd::pointnd(int dimensions = 3)
{
pointnd::dimensions = dimensions;
values = new int[dimensions];
}
pointnd::pointnd(int dimensions, int value)
{
pointnd::dimensions = dimensions;
values = new int[dimensions];
for (int i = 0; i < dimensions; i++)
values[i] = value;
}
pointnd::~pointnd()
{
delete[] values;
}
int &
pointnd::val(int dimension)
{
if (dimension > dimensions) {
cerr << "Point has only " << dimensions << " dimensions, not " << dimension << "\n";
exit(1);
}
return (values[dimension]);
}
void
pointnd::display()
{
for (int i = 0; i < dimensions; i++)
cout << values[i] << " ";
}
main()
{
pointnd a(3);
pointnd b(2, 0);
pointnd *c;
c = new pointnd[10];
a.val(0) = 8;
a.val(1) = 3;
a.val(2) = 4;
a.display();
cout << "\n";
b.display();
return (0);
}
- Η κλήση της συνάρτησης κατασκευής μπορεί να γίνει κατά τον
ορισμό μιας μεταβλητής με τη μορφή "κλάση μεταβλητή = κλάση(όρισμα)" ή
συνοπτικότερα με τη μορφή "κλάση μεταβλητή(όρισμα)".
Ιδιότητες και μέθοδοι της κλάσης
- Σε μια κλάση μπορούν να δηλωθούν (με τον προσδιορισμό static)
μεταβλητές οι οποίες υπάρχουν μόνο μια φορά για όλη την κλάση,
καθώς και συναρτήσεις που μπορούν
να κληθούν με τη σύνταξη κλάση::συνάρτηση.
Οι μεταβλητές αυτές χρησιμοποιούνται για την επεξεργασία στοιχείων
που αφορούν ολόκληρη την κλάση και όχι τα αντικείμενά της.
Οι συναρτήσεις που έχουν οριστεί static δεν έχουν πρόσβαση σε μη
static μεταβλητές ούτε στη μεταβλητή this.
Η αρχικοποίηση των μεταβλητών που έχουν δηλωθεί static πρέπει να γίνει
σε καθολικό επίπεδο, μια φορά για κάθε κλάση.
Το παρακάτω παράδειγμα ορίζει έναν μετρητή numpoints που μετρά πόσα
σημεία είναι ενεργά καθώς και την αντίστοιχη συνάρτηση πρόσβασης:
class point {
private:
int x, y;
static int numpoints;
public:
// ...
static int points_used();
};
int point::numpoints = 0;
// Constructors
point::point(int sx, int sy)
{
x = sx;
y = sy;
numpoints++;
}
point::point()
{
x = y = 0;
numpoints++;
}
// Destructor
point::~point()
{
numpoints--;
}
// Access function
int
point::points_used()
{
return (numpoints);
}
Συναρτήσεις friend
Σε μια κλάση μπορούν να οριστούν συναρτήσεις με τον προσδιορισμό
friend.
Οι συναρτήσεις αυτές ορίζονται σε καθολικό επίπεδο και μπορούν να
έχουν πρόσβαση στα στοιχεία private της κλάσης.
Οι συναρτήσεις friend χρησιμοποιούνται όταν η χρήση των συναρτήσεων
μέλους δεν είναι βολική, δηλαδή όταν η συνάρτηση πρέπει να έχει
πρόσβαση στα στοιχεία ενός αντικειμένου αλλά δε θέλουμε να
καλείται με τη σύνταξη της μεθόδου (αντικείμενο.μέθοδος()).
Παράδειγμα:
class point {
private:
int x, y;
static int numpoints;
public:
// ...
friend void display(point& p); // Display friend function
};
// Friend function; used as display(a)
void
display(point& p)
{
cout << "(" << p.x << "," << p.y << ")\n";
}
main()
{
point b = point(1, 2);
display(b); // Friend function
}
Ο προσδιορισμός const
Συναρτήσεις που δε μεταβάλλουν μέλη της κλάσης καλό είναι να δηλώνονται
και να ορίζονται ακολουθούμενες με τον προσδιορισμό const:
class point {
private:
int x, y;
public:
int getx() const; // Access functions
int gety() const;
void display(); // Display member function
// ...
};
int
point::getx() const
{
return (x);
}
Ο προσδιορισμός αυτός αναγκάζει το μεταγλωττιστή να ελέγχει αν η δέσμευση
αυτή τηρείται μέσα στο σώμα της συνάρτησης.
Παράδειγμα
Το παρακάτω παράδειγμα ορίζει και χρησιμοποιεί
την κλάση point που - εξεζητημένα - εκμεταλλεύεται όλα τα στοιχεία που
έχουν αναφερθεί:
point.h
class point {
private:
int x, y;
static int numpoints;
public:
point(); // Default contructor
point(int sx, int sy); // Other constructor
~point(); // Destructor
int getx() const; // Access functions
int gety() const;
void display(); // Display member function
void setpos(int sx, int sy); // Set position
static int points_used(); // Return number of points
friend double distance(point p1, point p2); // Display friend function
friend void display(point &p); // Display friend function
};
point.cpp
#include "point.h"
#include <iostream.h>
#include <math.h>
int point::numpoints = 0;
point::point(int sx, int sy)
{
x = sx;
y = sy;
numpoints++;
}
point::point()
{
x = y = 0;
numpoints++;
}
point::~point()
{
numpoints--;
}
int
point::getx() const
{
return (x);
}
int
point::gety() const
{
return (y);
}
// Member function; used as a.display();
void
point::display()
{
cout << "(" << x << "," << y << ")\n";
}
// Friend function; used as display(a)
void
display(point& p)
{
cout << "(" << p.x << "," << p.y << ")\n";
}
double
sqr(double x)
{
return (x * x);
}
double
distance(point p1, point p2)
{
return (sqrt(sqr(p1.x - p2.x) + sqr(p1.y - p2.y)));
}
void
point::setpos(int sx, int sy)
{
this->x = sx;
point::y = sy;
}
int
point::points_used()
{
return (numpoints);
}
main.cpp
#include <iostream.h>
#include "point.h"
main()
{
point a(1, 2);
point b, *c;
c = new point(5, 5);
b.setpos(6, 6);
a.display(); // Member function
display(b); // Friend function
c->display();
cout << "Distance from a to b = " << distance(a, b) << "\n";
cout << "points used = " << point::points_used() << "\n";
delete(c);
cout << "points used = " << point::points_used() << "\n";
return (0);
}
Φροντιστηριακή άσκηση
Να υλοποιηθεί σε C++ μια κλάση που να παριστάνει αντικείμενα
σε κίνηση με ιδιότητες την αρχική τους θέση και
την ταχύτητά τους στους άξονες x και y και τις παρακάτω μεθόδους:
- void setipos(double x, double y)
- Θέτει την αρχική θέση του αντικειμένου.
- void setvelocity(double x, double y)
- Θέτει την ταχύτητα x, y του αντικειμένου.
- double getxpos(int t)
- Επιστρέφει τη θέση x του αντικειμένου κατά τη χρονική στιγμή t.
- double getypos(int t)
- Επιστρέφει τη θέση y του αντικειμένου κατά τη χρονική στιγμή t.
Με βάση το παραπάνω να υλοποιηθεί πρόγραμμα που
- Ζητάει από το χρήστη τον
αριθμό των αντικειμένων που θέλει να δημιουργήσει.
- Για κάθε ένα από τα αντικείμενα ζητάει από το χρήστη την αρχική του θέση.
- Διαρκώς ζητάει από το χρήστη τον αύξοντα αριθμό ενός αντικειμένου,
την ταχύτητά του και ένα χρόνο t και εμφανίζει στην οθόνη τη θέση του κατά το
χρόνο t.
Σημείωση: η θέση κάθε αντικειμένου υπολογίζεται μόνο με βάση την αρχική του
θέση και την ταχύτητά του.
Παράδειγμα:
n=3
A1x=6
A1y=12
A2x=6
A2y=67
A3x=32
A3y=78
A=2
A2 vx=10
A2 vy=10
t=1
A2x=16 A2y=77
A=1
A1 vx=1
A1 vy=1
t=2
A1x=8 A2y=14
...
Ασκήσεις
Άσκηση 2
- Να υλοποιηθούν σε C++ οι κλάσεις andgate, orgate, notgate.
Οι κλάσεις αυτές θα προσομοιάζουν τις αντίστοιχες λογικές πύλες.
Κάθε κλάση να έχει μεθόδους που να θέτουν τις εισόδους (π.χ. seta, setb)
και μέθοδο που να επιστρέφει την τιμή της εξόδου (π.χ. getoutput).
- Με τη χρήση των παραπάνω κλάσεων (και μόνο) να υλοποιήσετε
μια κλάση που να προσομοιάζει έναν ημιαθροιστή.
Η κλάση αυτή να έχει μεθόδους που να θέτουν τις δύο εισόδους και
μεθόδους που να επιστρέφουν το άθροισμα και το κρατούμενο.
- Να γράψετε ένα πρόγραμμα σε C++ που να τυπώνει τους πίνακες
αλήθειας για τις παραπάνω κλάσεις.
- (Προαιρετικά) Με τη χρήση των παραπάνω κλάσεων να υλοποιήσετε
μια κλάση που να προσομοιάζει έναν πλήρη αθροιστή.
Η κλάση αυτή πρέπει να έχει μεθόδους που να θέτουν τις δύο εισόδους και το
κρατούμενο εισόδου καθώς και μεθόδους που να επιστρέφουν το άθροισμα και
το κρατούμενο εξόδου.
Με τη χρήση του πλήρη αθροιστή και τους τελεστές bit της C
μπορεί να υλοποιηθεί μια κλάση αθροιστή ακεραίων αριθμών (wordadder) με
τον παρακάτω τρόπο:
/*
* wordadder.h
*
* D. Spinellis, February 2000
*/
class wordadder {
private:
unsigned int a, b; // Input values
public:
void seta(unsigned int v); // Set input a
void setb(unsigned int v); // Set input b
unsigned int getsum(); // Return sum
};
/*
* wordadder.cpp
*
* D. Spinellis, February 2000
*/
#include "wordadder.h"
#include "fulladder.h"
void
wordadder::seta(unsigned int v)
{
a = v;
}
void
wordadder::setb(unsigned int v)
{
b = v;
}
unsigned int
wordadder::getsum()
{
fulladder fa[sizeof(unsigned int) * 8];
unsigned int i, bit;
unsigned int result = 0;
fa[0].setcarryin(false);
for (i = 0, bit = 1; bit; bit <<= 1, i++) {
fa[i].seta(a & bit);
fa[i].setb(b & bit);
if (bit << 1) // Do carry-over the last bit
fa[i + 1].setcarryin(fa[i].getcarryout());
if (fa[i].getsum())
result |= bit;
}
return (result);
}
Ο αθροιστής αυτός μπορεί να χρησιμοποιηθεί ως εξής:
wordadder add;
int a, b;
cin >> a;
cin >> b;
add.seta(a);
add.setb(b);
cout << a << "+" << b << "=" << add.getsum();
Δοκιμάστε το!
Καθοριζόμενοι τελεστές
Καθοριζόμενοι τελεστές
- Σε μια κλάση μπορούμε να ορίσουμε συναρτήσεις σε αντιστοιχία με
τους καθορισμένους τελεστές της C++ (+, -, *, /, ==, =, κ.λπ.)
με τη σύνταξη operator τελεστής(όρισμα).
- Το όρισμα πρέπει να περιέχει τόσες παραμέτρους όσες απαιτεί και
ο αντίστοιχος τελεστής. Αν η συνάρτηση είναι μέλος της κλάσης (και
όχι ορισμένη ως friend) τότε το πρώτο στοιχείο του ορίσματος περνάει
αυτόματα μέσω της this και δεν ορίζεται.
- Οι τελεστές που επιτρέπεται να οριστούν,
η προτεραιότητα των τελεστών και ο τρόπος που αυτοί συνδέονται
(αριστερά προς τα δεξιά ή ανάποδα) δεν μπορούν να μεταβληθούν.
- Το παρακάτω παράδειγμα ορίζει τον τελεστή + στην κλάση point,
επιτρέποντας την πρόσθεση σημείων:
#include <iostream.h>
class point {
private:
int x, y;
public:
point(int sx, int sy); // Constructor
point operator+(point b); // Overload the + operator
void display(); // Display member function
};
point::point(int sx, int sy)
{
x = sx;
y = sy;
}
void
point::display()
{
cout << "(" << x << "," << y << ")\n";
}
point
point::operator+(point b)
{
point p(x, y); // p is a new point at x, y
p.x += b.x;
p.y += b.y;
return (p);
}
main()
{
point p1 = point(1, 2);
point p2 = point(10, 20);
point p3 = point(0, 0);
p3 = p1 + p2;
p1.display();
p2.display();
p3.display();
}
Παράδειγμα: ασφαλείς πίνακες
Στη C και τη C++
οι δείκτες ενός πίνακα δεν ελέγχονται αν είναι συμβατοί με τις διαστάσεις
του πίνακα.
Έτσι το παρακάτω παράδειγμα δε θα εμφανίσει κάποιο λάθος κατά τη μεταγλώττιση
ή (πιθανά) και την εκτέλεση:
int a[20];
main()
{
a[50] = 42; // Out of bounds index
}
Το αποτέλεσμα είναι πως το παραπάνω πρόγραμμα μπορεί να γράψει σε θέσεις
μνήμης που φυλάσσονται άλλες μεταβλητές δημιουργώντας λάθη που είναι δύσκολο
να βρεθούν.
Στο επόμενο παράδειγμα, με τη χρήση καθοριζόμενων τελεστών, ορίζουμε την
κλάση intarray που υλοποιεί πίνακες ακεραίων μιας διάστασης.
Τα αντικείμενα της κλάσης αυτής μοιάζουν στη συμπεριφορά με πίνακες,
αλλά ελέγχουν αν ο δείκτης του πίνακα είναι συμβατός με τη διάσταση με την
οποία έχει οριστεί ο πίνακας.
#include <iostream.h>
#include <stdlib.h>
class intarray {
public:
intarray(unsigned int ssize = 10);
intarray(const intarray& sa);
~intarray(void);
intarray& operator=(const intarray& sa);
int& operator[](unsigned int i);
int elemnum();
protected:
unsigned int size; // Number of elements
int *values; // Values
};
intarray::intarray(unsigned int ssize)
{
size = ssize;
values = new int[ssize];
}
intarray::intarray(const intarray& sa)
{
size = sa.size;
values = new int[sa.size];
for (int i = 0; i < sa.size; i++)
values[i] = sa.values[i];
}
intarray::~intarray(void)
{
delete[] values;
}
intarray&
intarray::operator=(const intarray& sa)
{
delete values;
size = sa.size;
values = new int[size];
for (unsigned int i = 0; i < size; i++)
values[i] = sa.values[i];
return (*this);
}
int&
intarray::operator[](unsigned int i)
{
if (i >= size) {
cerr << "Array index " << i << " out of bounds\n";
exit(1);
}
return (values[i]);
}
int
intarray::elemnum()
{
return (size);
}
main()
{
intarray a(5);
a[2] = 8;
intarray b = a;
cout << b[2] << "\n"; // Will print 8
b[23] = 4; // Will exit with an error
return (0);
}
Είσοδος και έξοδος με τελεστές
Με καθορισμό μεθόδων για τους τελεστές << και >> μπορούμε να
ορίσουμε τον τρόπο που θα γίνεται η είσοδος και έξοδος για κλάσεις που ορίζουμε
εμείς.
Το παρακάτω παράδειγμα ορίζει μια κλάση date και τον τρόπο που αντικείμενα
της κλάσης αυτής θα εμφανίζονται στην οθόνη (ο ορισμός με τη
χρήση friend επιτρέπει στη συνάρτηση operator << να έχει ως πρώτο ορίσμα
μεταβλητή τύπου ostream).
#include <iostream.h>
class date {
private:
int month;
int day;
int year;
public:
date(int d, int m, int y);
friend ostream& operator<< (ostream& os, date& dt);
};
// Constructor
date::date(int d, int m, int y)
{
day = d;
month = m;
year = y;
}
// Overload << to define output
ostream& operator<< ( ostream& os, date& dt )
{
os << dt.day << '-' << dt.month << '-' << dt.year;
return os;
}
int
main()
{
date dt(23, 6, 2000); // Create a new date
cout << dt; // Output the date
return 0;
}
Ασκήσεις
Άσκηση 3 (προαιρετική)
- Να υλοποιηθεί σε C++ μια κλάση που να υποστηρίζει
πράξεις μεταξύ μιγαδικών αριθμών.
Με τη βοήθεια της κλάσης αυτής να υλοποιήσετε
μια αριθμομηχανή που να υποστηρίζει μιγαδικούς αριθμούς.
Κληρονομικότητα
Εισαγωγή
- Συχνά μια σειρά κλάσεων αντικειμένων μπορεί να μοντελοποιηθεί με
τη μορφή μιας ιεραρχίας.
- Για παράδειγμα:
- Όλα τα μέλη της Πανεπιστημιακής Κοινότητας είναι
φυσικά πρόσωπα και έχουν ως ιδιότητα το όνομα και το επώνυμό τους.
- Οι φοιτητές έχουν ακόμα ως ιδιότητα τον αριθμό μητρώου τους και
το έτος που φοιτούν.
- Όσοι έχουν σχέση εργασίας με το Πανεπιστήμιο έχουν ως ιδιότητα το
μισθό τους.
- Το διοικητικό προσωπικό έχει ως πρόσθετη ιδιότητα την ειδικότητά του.
- Οι διδάσκοντες έχουν ως πρόσθετη ιδιότητα τα μαθήματα που διδάσκουν.
Κληρονομικότητα σε κλάσεις
- Η ιεραρχία των κλάσεων εκφράζεται στη C++ με τον ορισμό μιας κλάσης που
είναι υποκλάση (subclass) μιας άλλης κλάσης (που
ονομάζεται βασική κλάση (base class)).
- Η σύνταξη για τον ορισμό αυτό είναι της μορφής:
class υποκλάση : public βασική_κλάση { ...
- Κάθε υποκλάση κληρονομεί (inherits) τις μεθόδους
και τις ιδιότητες που έχουν οριστεί ως public στη βασική κλάση.
- Η υποκλάση δεν έχει πρόσβαση στις μεθόδους και τις ιδιότητες
που έχουν οριστεί ως private στη βασική κλάση.
- Η υποκλάση μπορεί να ορίσει νέες ιδιότητες και μεθόδους και να
αντικαταστήσει ιδιότητες και μεθόδους που έχει ορίσει η βασική κλάση.
- Παράδειγμα:
class shape {
private:
int x, y; // Position
public:
void setposition(int x, int y);
};
void shape::setposition(int x, int y)
{
shape::x = x;
shape::y = y;
}
class circle : public shape {
private:
int radius;
public:
void setradius(int r);
};
class rectangle : public shape {
private:
int height, width;
public:
void setdimensions(int h, int w);
};
main()
{
circle c;
rectangle r;
r.setposition(1, 2);
c.setposition(3, 4);
}
- Μπορούμε να ορίσουμε μια άλλη κατηγορία μεθόδων και ιδιοτήτων
με όνομα protected. Αυτές είναι προσβάσιμες από τις υποκλάσεις της
κλάσης μας, αλλά όχι από άλλες συναρτήσεις.
Παράδειγμα:
class shape {
private:
int x, y;
protected:
int getx(); // Can be used by subclasses
int gety();
public:
void setposition(int x, int y);
}
- Ένας δείκτης σε μια υποκλάση μπορεί αυτόματα να μετατραπεί σε
δείκτη στη βασική της κλάση. Το αντίθετο δεν επιτρέπεται.
Παράδειγμα:
main()
{
circle c;
rectangle r;
shape *sp;
r.setposition(1, 2);
c.setposition(3, 4);
sp = &c; // Implici conversion
sp->setposition(3, 3); // Set the position of c
}
- Μια κλάση μπορεί να δηλωθεί με
πολλαπλή κληρονομικότητα (multiple inheritance) με
τη σύνταξη:
class υποκλάση : public βασική_κλάση1, public βασική_κλάση2, ... { ...
Στην περίπτωση αυτή η κλάση κληρονομεί τα μέλη και τις μεθόδους όλων
των βασικών κλάσεων που δηλώθηκαν.
Ιδεατές συναρτήσεις
- Αν σε μια κλάση δηλώσουμε μια συνάρτηση ως virtual τότε οι
υποκλάσεις της κλάσης αυτής μπορούν να αντικαταστήσουν τη συνάρτηση
με μια που θα ορίσουν αυτές.
- Όταν κληθεί η συνάρτηση που έχει αντικατασταθεί από μια υποκλάση
μέσω ενός δείκτη της βασικής κλάσης ο οποίος έχει προέλθει από δείκτη
κάποιας υποκλάσης τότε θα κληθεί η αντίστοιχη συνάρτηση της υποκλάσης
από την οποία έχει προέλθει ο δείκτης.
- Η δυνατότητα αυτή επιτρέπει:
- το δυναμικό καθορισμό της συμπεριφοράς ενός αντικειμένου ανάλογα με
την κλάση του κατά την εκτέλεση του προγράμματος,
- την αλλαγή της συμπεριφοράς μιας παλιάς κλάσης από μια νεώτερη
(υποκλάση της) και
- την ενοποιημένη διαχείριση διαφορετικών αντικειμένων μέσω της βασικής
τους κλάσης.
- Η δυνατότητα αυτή προάγει τη C++ από γλώσσα που υποστηρίζει τα
αντικείμενα σε αντικειμενοστρεφή γλώσσα.
Παράδειγμα
Το παρακάτω παράδειγμα ορίζει τη βασική κλάση shape και τις υποκλάσεις
της circle και rectangle.
Η μέθοδος area ορίζεται ως virtual με αποτέλεσμα να μπορεί να οριστεί
για τις υποκλάσεις και κατά την εκτέλεση του προγράμματος να εκτελεστεί
η σωστή έκδοσή της.
Η συνάρτηση printarea της shape εκμεταλλεύεται τη δυνατότητα αυτή και
μπορεί να κληθεί (και να δουλέψει σωστά) με όρισμα οποιαδήποτε από τις
υποκλάσεις της shape.
#include <iostream.h>
class shape {
private:
double x, y; // Position
public:
void setposition(double x, double y);
void printarea();
virtual double area();
};
double
shape::area()
{
return (0);
}
void
shape::printarea()
{
cout << area() << "\n";
}
void shape::setposition(double x, double y)
{
shape::x = x;
shape::y = y;
}
class circle : public shape {
private:
double radius;
public:
void setradius(double r);
virtual double area();
};
void
circle::setradius(double r)
{
radius = r;
}
double
circle::area()
{
return (3.1415927 * radius * radius);
}
class rectangle : public shape {
private:
double height, width;
public:
void setdimensions(double h, double w);
virtual double area();
};
void
rectangle::setdimensions(double h, double w)
{
height = h;
width = w;
}
double
rectangle::area()
{
return (height * width);
}
main()
{
circle c;
rectangle r;
shape *sp;
r.setposition(1, 2);
c.setposition(3, 4);
r.setdimensions(10, 10);
c.setradius(1);
c.printarea();
r.printarea();
sp = &r;
cout << sp->area();
}
Αφηρημένες κλάσεις
Για παράδειγμα, ένας σχεδιασμός του πληροφοριακού συστήματος του
Πανεπιστημίου μπορεί να ορίσει την αφηρημένη κλάση person ως βασική
κλάση για τις υποκλάσεις student, employee και visitor.
Αν και δε θα μπορεί να οριστεί μια μεταβλητή για την αφηρημένη κλάση
person, αυτή μπορεί να περιέχει ορισμένα βασικά χαρακτηριστικά όπως
birth_date και να επιβάλει την υλοποίηση συγκεκριμένων μεθόδων
όπως home_page_URL() ορίζοντάς τις ως θεωρητικές.
Ασκήσεις
Άσκηση 4 (προαιρετική)
- Να υλοποιήσετε σε C++ τις κλάσεις icsd_student και math_student.
Οι κλάσεις αυτές πρέπει να έχουν ως ιδιότητα τον αριθμό μαθημάτων
που έχει περάσει ένας φοιτητής, αντίστοιχες μεθόδους
πρόσβασης στην ιδιότητα αυτή και μια μέθοδο που να επιστρέφει αληθές
αν ο φοιτητής μπορεί να πάρει πτυχίο (έχοντας περάσει 40 μαθήματα
για την icsd_student και 38 για την math_student).
Να χρησιμοποιήσετε κληρονομικότητα για να ελαχιστοπιήσετε τις
μεθόδους των κλάσεων.
- Γράψτε ένα μικρό πρόγραμμα που να ελέγχει τη λειτουργία των κλάσεων αυτών.
Δείκτες σε μέλη, ενώσεις
Δείκτες σε μέλη κλάσεων
- Η C++ επιτρέπει τον ορισμό δεικτών σε μέλη (μεταβλητές ή συναρτήσεις)
μιας κλάσης.
Στο παρακάτω παράδειγμα η μεταβλητή coordptr ορίζεται ως δείκτης
σε ακέραιες μεταβλητές - μέλη - της κλάσης point.
class point {
public:
int x, y;
};
int point::*coordptr; // Pointer to one of the two point coordinates
Η μεταβλητή coordptr μπορεί να δείχνει στο μέλος x ή στο μέλος y.
- Η μεταβλητή μπορεί να λάβει αρχική τιμή με τη σύνταξη
var = &(class_name::member_name);
Παράδειγμα:
coordptr = &(point::x); // Coordptr points to the x coordinates
- Πρόσβαση στο αντίστοιχο μέλος μπορεί να υπάρξει μόνο με βάση
ένα πραγματικό αντικείμενο ή δείκτη της συγκεκριμένης κλάσης με
τους τελεστές .* και ->* αντίστοιχα.
Αριστερά από τον τελεστή γράφεται το αντικείμενο ή ο δείκτης της
κλάσης και δεξιά ο δείκτης στο μέλος της κλάσης.
- Το παρακάτω παράδειγμα ορίζει δύο μεταβλητές τύπου point και
δίνει αρχικές τιμές στα μέλη τους μέσω της coordptr η οποία αρχικά
δείχνει στα x και μετά στα y.
Έτσι, στο τέλος τυπώνει τις τιμές των σημείων ως (1, 2) και (10, 20).
#include <iostream.h>
class point {
public:
int x, y;
void print() { cout << x << "," << y << "\n"; }
};
main()
{
int point::*coordptr; // Pointer to a point coordinate
point p, p2;
coordptr = &(point::x); // Coordptr points to the x coordinates
p.*coordptr = 1;
p2.*coordptr = 10;
coordptr = &(point::y); // Coordptr now points to the y coordinates
p.*coordptr = 2;
p2.*coordptr = 20;
p.print();
p2.print();
return (0);
}
Δομές και ενώσεις
Η δήλωση μιας δομής στη C++ με τη μορφή:
struct s { ...
είναι μια συντομογραφία για τη δήλωση:
class s {public: ...
δηλαδή για μια κλάση της οποίας όλα τα μέλη είναι δημόσια.
Αντίστοιχα η δήλωση μιας ένωσης (union) επιτρέπει
τη χρήση του ίδιου χώρου από όλα τα μέλη της ένωσης.
Η C++ επιτρέπει τον ορισμό συναρτήσεων κατασκευής ως μέλη της ένωσης.
Με τη χρήση πολυμορφικών συναρτήσεων κατασκευής μπορεί κανείς να
αποδώσει αρχική τιμή σε οποιοδήποτε μέλος της ένωσης κατά την
αρχικοποίησή του.
Παράδειγμα:
union token {
char c;
int i;
double d;
token(char sc) {c = sc;}
token(int si) {i = si;}
token(double sd) {d = sd;}
};
void
f()
{
token a = 3.14; // Assign value to a.d
token b = 't'; // Assign value to b.c
}
Σε περίπτωση που θέλουμε να αποφύγουμε λάθη με την ανάθεση σε ένα
μέλος ενός τύπου και την ανάκτηση από ένα μέλος άλλου τύπου μπορούμε
να εμφωλιάσουμε την ένωση σε μια κλάση:
class token {
private:
enum e_type {CHAR, INT, DOUBLE} type;
union {
char c;
int i;
double d;
};
void check(e_type t) {if (type != t) error();}
public:
token(char sc) {c = sc; type = CHAR;}
token(int si) {i = si;} type = INT;}
token(double sd) {d = sd;} type = DOUBLE;}
int &cval() {check(CHAR); return c;}
int &ival() {check(INT); return i;}
int &dval() {check(DOUBLE); return d;}
token(char sc) {c = sc;}
token(int si) {i = si;}
token(double sd) {d = sd;}
};
void
f()
{
token a = 3.14; // Assign value to a.d
printf("%g\n", a.dval()); // Ok
printf("%d\n", a.ival()); // Check will fail
}
Αντικειμενοστρεφής σχεδιασμός με UML
Εισαγωγή
Η ενοποιημένη γλώσσα σχεδιασμού (unified modeling language)
(UML) είναι μια γραφική γλώσσα για την οπτική παράσταση,
τη διαμόρφωση προδιαγραφών και την τεκμηρίωση συστημάτων που βασίζονται
σε λογισμικό.
Η UML στοχεύει στο σχεδιασμό αντικειμενοστρεφών συστημάτων.
Το σχέδιο είναι μια απλοποιημένη παράσταση της πραγματικότητας.
Σχεδιάζουμε για να μπορέσουμε να καταλάβουμε το σύστημα που αναπτύσσουμε.
Έτσι δημιουργώντας ένα σχέδια επιτυγχάνουμε τέσσερις στόχους:
- παριστάνουμε οπτικά το σύστημα που έχουμε ή θέλουμε να κατασκευάσουμε,
- προσδιορίζουμε τη δομή και τη συμπεριφορά του συστήματος,
- δημιουργούμε ένα πρότυπο για να βασίσουμε την κατασκευή του συστήματος,
- τεκμηριώνουμε τις αποφάσεις που λάβαμε.
Σε όλους τους τεχνολογικούς τομείς ο σχεδιασμός βασίζεται σε τέσερις
βασικές αρχές:
- η επιλογή του είδους του σχεδίου έχει επίπτωση στον τρόπο και την
μορφή επίλυσης του προβλήματος,
- όλα τα σχέδια εκφράζονται σε διαφορετικές βαθμίδες ακρίβειας,
- τα καλύτερα σχέδια σχετίζονται με την πραγματικότητα,
- ένα είδος σχεδίων δεν είναι ποτέ αρκετό.
Η UML περιλαμβάνει τρία βασικά στοιχεία:
- Οντότητες
- Σχέσεις
- Διαγράμματα
Η UML είναι μια πλήρης και πλούσια γλώσσα με εξαιρετικά ευρύ πεδίο εφαρμογής.
Στο μάθημα αυτό θα εξετάσουμε εξαιρετικά συνοπτικά τον τρόπο παράστασης
ορισμένων αντικειμενοστρεφών δομών σε UML.
Κλάσεις
- Μια κλάση σχεδιάζεται ως ένα ορθογώνιο με τρία τμήματα όπως
στο παρακάτω σχήμα:
- Τα ονόματα των ιδιοτήτων και μεθόδων της κλάσης μπορούν να
έχουν ένα πρόθεμα ανάλογα με την ορατότητά τους:
- +
- public
- #
- protected
- -
- private
- Οι μεταβλητές και οι συναρτήσεις που αναφέρονται στην κλάση
και όχι σε αντικείμενά της (static στη C++)
σχεδιάζονται υπογραμμισμένες.
- Οι αφηρημένες κλάσεις έχουν το όνομά τους γραμμένο με πλάγια στοιχεία.
- Το σχήμα που ακολουθεί αποτελεί σχέδιο για μια απλή κλάση στοίβας
ακεραίων:
Σχέσεις
Στη UML ορίζονται τρεις βασικές σχέσεις:
- εξάρτηση (dependency)
- γενίκευση (generalisation)
- σύνδεση (association)
Εξάρτηση
Η εξάρτηση δηλώνει πως μια αλλαγή σε μιαν οντότητα θα επηρεάσει μιαν άλλη
αλλά όχι απαραίτητα και το αντίστροφο.
Παριστάνεται με μια διακεκομμένη γραμμή με ανοιχτό βέλος που δείχνει προς
την οντότητα που υπάρχει εξάρτηση:
Γενίκευση
Η γενίκευση δηλώνει μια σχέση ανάμεσα σε κάτι γενικό
(τη βασική κλάση ή αλλιώς γονέα)
και κάτι ειδικό
(μιαν υποκλάση ή αλλιώς παιδί της).
Παριστάνεται με μια συνεχή γραμμή με κλειστό βέλος που δείχνει προς
τη βασική κλάση:
Σύνδεση
Η σύνδεση αναφέρεται σε αντικείμενα τα οποία συνδέονται με κάποιο τρόπο
με άλλα.
Όταν δύο κλάσεις είναι συνδεδεμένες μπορεί κανείς να μεταβεί από αντικείμενα
της μιας σε αντικείμενα της άλλης.
Η σύνδεση παριστάνεται με μια ευθεία γραμμή ανάμεσα στα δύο αντικείμενα.
- Αν η σύνδεση δεν είναι αμφίδρομη τότε η κατεύθυνσή της μπορεί να οριστεί
με ένα ανοιχτό βέλος.
- Το όνομα της σύνδεσης μπορεί να γραφεί πάνω από τη γραμμή, ενώ
η κατεύθυνση του ονόματος ορίζεται από ένα βέλος πλάι στο όνομα.
- Ο ρόλος των οντοτήτων που συνδέονται προσδιορίζεται από ένα όνομα
στην κάθε άκρη της γραμμής.
- Ο αριθμός που δηλώνει πόσα αντικείμενα αντιστοιχούν σε κάθε αντικείμενο
στην άλλη άκρη της σχέσης (πολλαπλότητα (multiplicity))
δηλώνεται από έναν αριθμό (π.χ. 3), ή μια περιοχή αριθμών (π.χ. 1..* για ένα
έως πολλά) πάνω από την αντίστοιχη άκρη της γραμμής.
Αν σε μια σχέση τα αντικείμενα απαρτίζουν τμήματα ενός όλου, τότε
αυτή απεικονίζεται ως συγκρότημα (aggregation)
με την παράσταση ενός διαμαντιού στην άκρη του "όλου".
Αν σχέση τα αντικείμενα που απαρτίζουν τμήματα ενός όλου έχουν
την ίδια διάρκεια ζωής με το όλο, τότε
αυτή απεικονίζεται ως σύνθεση (composition)
με την παράσταση ενός γεμάτου διαμαντιού στην άκρη του "όλου".
Είδη εξαρτήσεων
Με τη χρήση μιας εξάρτησης εκφράζουμε σημασιολογικές (semantic)
σχέσεις ανάμεσα σε στοιχεία του μοντέλου.
Σε τέτοιες περιπτώσεις μια αλλαγή σε ένα στοιχείο της εξάρτησης
μπορεί να έχει επίπτωση στο άλλο.
Διακρίνουμε τα παρακάτω είδη εξάρτησης:
- πρόσβαση (access)
-
Άδεια σε κάποια στοιχεία από ένα τμήμα να έχουν πρόσβαση σε στοιχεία
από άλλο τμήμα (access).
- σύνδεση (binding)
-
Παροχή τιμών σε ένα πρότυπο για να δημιουργήσει ένα νέο στοιχείο (bind).
- κλήση (call)
-
Μια μέθοδος καλεί μια άλλη (call).
- απόρρεια (derivation)
-
Ένα στοιχείο που μπορεί να υπολογιστεί από κάποιο άλλο (derive).
- friend
-
Ένα στοιχείο μπορεί να έχει πρόσβαση σε στοιχεία άλλης κλάσης παρά
τους όποιους περιορισμούς (friend).
- εισαγωγή (import)
-
Άδεια σε ένα τμήμα να εισάγει και να χρησιμοποιήσει τα στοιχεία
ενός άλλου τμήματος (import).
- δημιουργία (instantiation)
-
Μια μέθοδος μιας κλάσης δημιουργεί αντικείμενα μιας άλλης κλάσης (instantiate).
- παράμετρος (parameter)
-
Σχέση ανάμεσα σε μια λειτουργία και τις παραμέτρους της (parameter).
- δημιουργία (realization)
-
Σχέση ανάμεσα σε μια προδιαγραφή και την υλοποίησή της (realize).
- εκλέπτυνση (refinement)
-
Δήλωση για την ύπαρξη απεικόνισης ανάμεσα σε δύο σημασιολογικά επίπεδα (refine).
- αποστολή (send)
-
Σχέση ανάμεσα στον αποστολέα και τον παραλήπτη ενός μηνύματος (send).
- ίχνος (trace)
-
Σχέση ανάμεσα σε δύο στοιχεία δύο διαφορετικών μοντέλων που δεν αποτελεί
όμως απεικόνιση (trace).
- χρήση (usage)
-
Ένα στοιχείο απαιτεί την ύπαρξη ενός άλλου στοιχείο για τη λειτουργία του
(π.χ. call, instantiate, parameter, send) (use).
Στο διάγραμμα της UML γράφουμε μέσα σε εισαγωγικά το είδος της εξάρτησης
(αυτό που εμφανίζεται στο τέλος κάθε ορισμού στον παραπάνω πίνακα)
πάνω από την αντίστοιχη γραμμή με το βέλος:
Διάγραμμα κλάσεων
Το διάγραμμα των κλάσεων ενός συστήματος περιέχει τις κλάσεις μαζί με
του αντίστοιχους δεσμούς εξάρτησης, γενίκευσης και σύνδεσης.
Έτσι ένα διάγραμμα κλάσεων μπορεί να απεικονίσει τη χρήση της
κληρονομικότητας στο σχεδιασμό με τη χρήση δεσμών γενίκευσης όπως
στο παρακάτω σχήμα:
Διάγραμμα αντικειμένων
Τα διαγράμματα αντικειμένων χρησιμοποιούνται για το σχεδιασμό της
στατικής κατάστασης του συστήματος κατά μια συγκεκριμένη χρονική στιγμή.
Κάθε αντικείμενο σχεδιάζεται ως ένα ορθογώνιο με την παρακάτω μορφή:
Το σύνολο των αντικειμένων σχεδιάζεται με βάση τους συνδέσμους που
ορίζονται πάνω σε αυτό.
Βιβλιογραφία
Άσκηση
Άσκηση 5
Η άσκηση αυτή πρέπει να παραδωθεί σε γραπτή μορφή στη Γραμματεία μέχρι την
Τρίτη 27 Μαρτίου 2001 11:59 πμ.
Για κάθε μέρα καθυστέρησης θα αφαιρείται ένας βαθμός
από τον τελικό βαθμό της άσκησης.
Στη βαθμολογία θα ληφθεί υπόψη και η εμφάνιση του παραδοτέου.
- Να σχεδιάσετε σε UML τις κλάσεις icsd_student και math_student.
Οι κλάσεις αυτές πρέπει να έχουν ως ιδιότητα τον αριθμό μαθημάτων
που έχει περάσει ένας φοιτητής, αντίστοιχες μεθόδους
πρόσβασης στην ιδιότητα αυτή και μια μέθοδο που να επιστρέφει αληθές
αν ο φοιτητής μπορεί να πάρει πτυχίο (έχοντας περάσει 40 μαθήματα
για την icsd_student και 38 για την math_student).
Να χρησιμοποιήσετε κληρονομικότητα για να ελαχιστοπιήσετε τις
μεθόδους των κλάσεων.
- Ορισμένες φορές χρειάζεται να ανακτήσουμε το σχεδιασμό ενός
συστήματος από την υλοποίησή του.
Η διαδιακασία αυτή καλείται
αντίστροφος σχεδιασμός (reverse engineering)
και απαιτείται όταν δεν υπάρχει ο αρχικός σχεδιασμός ή αυτός δεν
έχει συντηρηθεί κατά τη διάρκεια ζωής του έργου.
Για το παρακάτω σύνολο από αρχεία C++
που δηλώνουν αντίστοιχες κλάσεις
να σχεδιάσετε σε UML ένα διάγραμμα που να απεικονίζει την κάθε κλάση
και τη σχέση της με τις υπόλοιπες.
(Τα αρχεία αυτά υλοποιούν ένα σύστημα μετατροπής χαρακτήρων ανάμεσα σε διάφορα
πρότυπα κωδικοποίησης, μεταγραφής και μεταγραμματισμού.)
Προσοχή: για την διαδικασία του αντίστροφου σχεδιασμού δεν απατείται
η πλήρης κατανόηση του κώδικα.
filter.h
#ifndef FILTER_
#define FILTER_
class filter {
protected:
filter *input;
public:
virtual int getcharacter() = 0;
void setinput(filter *i) {input = i;};
};
#endif
htmll1.h
#ifndef HTMLL1_
#define HTMLL1_
#include "filter.h"
#include "queue.h"
class htmll1: public filter, public queue {
private:
void fillbuff(); // Fill the queue buffer
public:
int getcharacter() { return (queue::getcharacter()); };
};
#endif
lex.h
#ifndef LEX_
#define LEX_
#include "filter.h"
#include "queue.h"
class lex: public filter, public queue {
private:
void (*lexfun)(lex *lf);
void fillbuff() { lexfun(this); }; // Fill the queue buffer
public:
lex(void (*lf)(lex *l)) { lexfun = lf; };
inline int getinputcharacter() { return input->getcharacter(); };
int getcharacter() { return (queue::getcharacter()); };
};
#endif
map.h
#ifndef MAP_
#define MAP_
#include "filter.h"
class map: public filter {
private:
int mapsize; // Size of character map
int *charmap; // Map from source to target
char default_char; // Default map character
public:
map(char *source, char *target, char dflt);
int getcharacter();
};
#endif
queue.h
#ifndef QUEUE_
#define QUEUE_
#include "filter.h"
class queue {
private:
int *q; // Output queue
int qlen; // Characters in output queue
int qhead;
int qtail;
virtual void fillbuff() = 0; // Fill the queue buffer
public:
void nq(int c); // Add a character to the output queue
unsigned int getcharacter(); // Get a character from the output queue
queue(const int qlen = 100);
~queue() { delete[] q; };
};
#endif
stdinput.h
#ifndef STDINPUT_
#define STDINPUT_
#include "filter.h"
#include <iostream.h>
class stdinput: public filter {
public:
int getcharacter()
{
unsigned char c;
cin.read(&c, 1);
return (cin.eof() ? EOF : c);
};
};
#endif
translit.h
#ifndef TRANSLIT_
#define TRANSLIT_
#include "filter.h"
#include "queue.h"
class translit: public filter, public queue {
private:
void fillbuff(); // Fill the queue buffer
public:
int getcharacter() { return (queue::getcharacter()); };
};
#endif
ucs2i.h
#ifndef UCS2I_
#define UCS2I_
#include "filter.h"
class ucs2i: public filter {
public:
int getcharacter()
{
int c1, c2;
c1 = input->getcharacter();
c2 = input->getcharacter();
return ((c1 << 8) | c2);
}
};
#endif
ucs2o.h
#ifndef UCS2O_
#define UCS2O_
#include "filter.h"
#include "queue.h"
class ucs2o: public filter, public queue {
private:
void fillbuff() // Fill the queue buffer
{
unsigned int c;
c = input->getcharacter();
nq(c >> 8);
nq(c & 0xff);
}
public:
int getcharacter() { return (queue::getcharacter()); };
};
#endif
utf7.h
#ifndef UTF7_
#define UTF7_
// See RFC 1642
class utf7 {
protected:
static char base64[];
static short invbase64[128];
static char direct[];
static char optional[];
static char spaces[]; /* space, tab, return, line feed */
static char mustshiftsafe[128];
static char mustshiftopt[128];
static const int SHIFT_IN;
static const int SHIFT_OUT;
unsigned long BITbuffer;
unsigned long buffertemp;
int bufferbits;
inline void write_n_bits(int x, int n)
{
BITbuffer |= ((x & ~(-1L<<n)) << (32-n-bufferbits));
bufferbits += n;
}
inline int read_n_bits(int n)
{
buffertemp = (BITbuffer >> (32-n));
BITbuffer <<= n;
bufferbits -= n;
return (buffertemp);
}
public:
utf7();
};
#endif
utf7i.h
#ifndef UTF7I_
#define UTF7I_
#include "filter.h"
#include "queue.h"
#include "utf7.h"
// See RFC 1642
class utf7i: public filter, public queue, public utf7 {
private:
void fillbuff();
int shifted, first, wroteone;
public:
utf7i() { shifted = first = wroteone = 0; };
int getcharacter() { return (queue::getcharacter()); };
};
#endif
utf7o.h
#ifndef UTF7O_
#define UTF7O_
#include "filter.h"
#include "queue.h"
#include "utf7.h"
// See RFC 1642
class utf7o: public filter, public queue, public utf7 {
private:
char *mustshift;
int shifted, needshift, done;
void fillbuff();
public:
int getcharacter() { return (queue::getcharacter()); };
void optional(bool opt);
utf7o()
{
mustshift = mustshiftopt;
shifted = needshift = done = 0;
}
};
#endif
utf8i.h
#ifndef UTF8I_
#define UTF8I_
#include "filter.h"
class utf8i: public filter {
public:
int getcharacter()
{
int c1, c2, c3, c4;
int cout;
c1 = input->getcharacter();
if ((c1 & 0x80) == 0) {
cout = c1;
} else if ((c1 & 0xe0) == 0xc0) {
c2 = input->getcharacter();
cout = (c2 & 0x3f) | ((c1 & 0x1f) << 6);
} else if ((c1 & 0xf0) == 0xe0) {
c2 = input->getcharacter();
c3 = input->getcharacter();
cout = ((c1 & 0xf) << 12) |
((c2 & 0x3f) << 6) |
(c3 & 0x3f);
} else {
error("UTF-8 input: UCS characters above 0xffff are not supported\n");
}
return (cout);
}
};
#endif
utf8o.h
#ifndef UTF8O_
#define UTF8O_
#include "filter.h"
#include "queue.h"
class utf8o: public filter, public queue {
private:
void fillbuff() // Fill the queue buffer
{
unsigned int c;
c = input->getcharacter();
if (c < 0x80) {
nq(c);
} else if (c < 0x800) { // bbb bbbb bbbb
nq(0xC0 | c>>6); // 110b bbbb
nq(0x80 | c & 0x3F); // 10bb bbbb
} else if (c < 0x10000) { // bbbb bbbb bbbb bbbb
nq(0xE0 | c>>12); // 1110 bbbb
nq(0x80 | c>>6 & 0x3F); // 10bb bbbb
nq(0x80 | c & 0x3F); // 10bb bbbb
} else {
error("UCS characters above 0xffff are not supported\n");
}
}
public:
int getcharacter() { return (queue::getcharacter()); };
};
#endif
Πρότυπα στη C++
Παραμετροποίηση προγραμμάτων με πρότυπα
Η C++ μας επιτρέπει να δηλώσουμε και να ορίσουμε
συναρτήσεις και κλάσεις παραμετροποιημένες
ως προς τους τύπους ή τις σταθερές που χρησιμοποιούν.
Βάση για τους ορισμούς αυτούς είναι ένα πρότυπο (template).
Έχοντας ορίσει μια συνάρτηση ή μια κλάση με μορφή προτύπου μπορούμε στη
συνέχεια να τη χρησιμοποιήσουμε για κάποιο συγκεκριμένο τύπο ή σταθερά.
Η χρήση αυτή γίνεται αυτόματα μέσα στον πηγαίο κώδικα.
Το παρακάτω παράδειγμα ορίζει και
χρησιμοποιεί μια συνάρτηση max παραμετροποιημένη
ως προς τον τύπο των παραμέτρων και του αποτελέσματός της:
#include <iostream.h>
/*
* Return the maximum of a, b.
* A, b and the return type of max are of type T.
*/
template <class T>
T
max(T a, T b)
{
if (a > b)
return (a);
else
return (b);
}
main()
{
cout << max("Samos", "Zambia") << "\n";
cout << max(3, 42) << "\n";
cout << max(3.1415, 1.4142) << "\n";
}
Χρησιμοποιούμε πρότυπα:
- για να ορίσουμε μια δομή δεδομένων η οποία να δουλεύει για
οποιοδήποτε τύπο θέλουμε,
- για να ορίσουμε πολυμορφικές συναρτήσεις που να δουλεύουν
σε οποιοδήποτε τύπο τις χρησιμοποιήσουμε,
- για να επιτύχουμε έλεγχο τύπων σε συναρτήσεις των οποίων
οι παράμετροι θα ήταν τύπου void * (π.χ. qsort, bsearch).
Σε αντίθεση με πολυμορφικές συναρτήσεις που ορίζονται με υπερφόρτωση,
συναρτήσεις που ορίζονται με πρότυπα απαιτούν έναν μόνο ορισμό για
όλους τους πιθανούς τύπους.
Ο μεταγλωττιστής αυτόματα υλοποιεί την κάθε συνάρτηση για το συγκεκριμένο
τύπο που χρησιμοποιείται.
Με τη χρήση των προτύπων αυξάνουμε την ευελιξία του κώδικα που γράφουμε και
μειώνουμε το μέγεθος του κώδικα που απαιτείται για μια συγκεκριμένη υλοποίηση.
Ο ορισμός του προτύπου περιέχει μετά τη δεσμευμένη λέξη
template μια σειρά τύπων ή παραμέτρων μέσα σε < >
που χρησιμοποιούμε για να ορίσουμε τη συγκεκριμένη συνάρτηση ή κλάση.
Οι τύποι και οι παράμετροι χωρίζονται μεταξύ τους με κόμμα.
Οι τύπου μπορούν να οριστούν ως class όνομα_κλάσης
(π.χ. class T) ή ως typename όνομα_τύπου (π.χ. typename T).
Οι παράμετροι ορίζονται όπως και στις συναρτήσεις (π.χ. int stacksize).
Μέσα στη δήλωση της συνάρτησης ή της κλάσης καθώς και στον ορισμό του
σώματός τους μπορούμε να χρησιμοποιήσουμε τα ονόματα που δηλώθηκαν με
βάση το πρότυπο σαν να ήταν πραγματικοί τύποι ή σταθερές.
Κατά τη χρήση μια κλάσης ή συνάρτησης οι παράμετροι πρέπει να είναι
σταθερές.
Το παρακάτω παράδειγμα δηλώνει την πρότυπη κλάση tstack
ως μια στοίβα της οποίας ο τύπος
των στοιχείων και το μέγεθος ορίζονται με βάση το πρότυπό της και
το αντικείμενο instack ως μια στοίβα 20 ακεραίων:
template <class T, int i> class tstack
{
T data[i];
int items;
public:
tstack(void);
void push(T item);
T pop(void);
};
tstack <int, 20> intstack;
Η υλοποίηση προτύπων που χρησιμοποιούμε
(Microsoft Visual C++ 5.0)
απαιτεί ο ορισμός του προτύπου να
είναι στο ίδιο αρχείο με τη χρήση του.
Για το λόγο αυτό οι κλάσεις και οι συναρτήσεις που ορίζονται με βάση τα
πρότυπα ορίζονται και δηλώνονται μέσα σε αρχείο επικεφαλίδων.
Πρότυπα συναρτήσεων
- Ο ορισμός συναρτήσεων με βάση ένα πρότυπο γίνεται με τη
σύνταξη:
template <δηλώσεις τύπων>
δήλωση συνάρτησης με βάση τους τύπους
Το παρακάτω παράδειγμα ορίζει τη συνάρτηση swap που εναλλάσσει μεταξύ τους
τις δύο μεταβλητές - ορίσματα της συνάρτησης:
// File tswap.h
template <class T>
void
swap(T &a, T &b)
{
T c;
c = a;
a = b;
b = c;
}
- Η υλοποίηση της συνάρτησης γίνεται αυτόματα για τον τύπο για τον
οποίο χρησιμοποιείται:
// File swaptest.cpp
#include <iostream.h>
#include "tswap.h"
main()
{
int a = 1, b = 2;
double da = 1.1, db = 2.2;
swap(a, b); // Swap two integers
swap(da, db); // Swap two floating point numbers
cout << a << "," << b << "\n";
cout << da << "," << db << "\n";
}
- Μπορούμε ακόμα να καθορίσουμε κατά τη χρήση της συνάρτησης τον
τύπο της συνάρτησης που θέλουμε να χρησιμοποιηθεί αρκεί οι παράμετροι
να είναι συμβατοί (ακόμα και μέσω μετατροπών) με το συγκεκριμένο τύπο.
Το παρακάτω παράδειγμα θα τυπώσει 3:
#include <iostream.h>
template <typename T>
T
max(T a, T b)
{
if (a > b)
return (a);
else
return (b);
}
main()
{
cout << max<int>(3.1415, 1.4142) << "\n";
}
Πρότυπα κλάσεων
- Ο ορισμός κλάσεων με βάση πρότυπα επιτρέπει τον ορισμό συλλογών δεδομένων
(όπως στοίβες, ουρές, πίνακες, κ.λπ.) ανεξάρτητα από το τελικό περιεχόμενό
τους.
- Όταν ορίζουμε μεταβλητές με βάση μια τέτοια πρότυπη κλάση πρέπει
να δίνουμε και συγκεκριμένη τιμή σε όλες τις παραμέτρους του προτύπου
(π.χ. τον τύπο των στοιχείων που θέλουμε να αποθηκεύσουμε).
- Το παρακάτω παράδειγμα ορίζει μια πρότυπη κλάση για στοίβα με
πεπερασμένο μέγεθος καθώς και δύο αντικείμενα της κλάσης αυτής,
μια στοίβα 20 ακεραίων και μια από 10 double.
#include <iostream.h>
#include <assert.h>
template <class T, int i>
class tstack
{
private:
T data[i];
int items;
public:
tstack();
void push(T item);
T pop(void);
void print(void);
};
// Constructor
template <class T, int i>
tstack<T, i>::tstack(void)
{
items = 0;
}
template <class T, int i>
void
tstack<T, i>::push(T item)
{
assert(items < i);
data[items++] = item;
}
template <class T, int i>
T
tstack<T, i>::pop(void)
{
assert(items > 0);
return (data[--items]);
}
main()
{
tstack <int, 20> int_stack;
tstack <double, 10> double_stack;
int_stack.push(7);
int_stack.push(45);
double_stack.push(3.14);
double_stack.push(1.41);
cout << int_stack.pop() << "\n";
cout << int_stack.pop() << "\n";
cout << double_stack.pop() << "\n";
cout << double_stack.pop() << "\n";
}
Άσκηση
Άσκηση 6
- Να παραμετροποιήσετε με τη χρήση προτύπων τους
ασφαλείς πίνακες που παρουσιάζονται ως παράδειγμα καθοριζόμενων τελεστών.
- Γράψτε ένα μικρό πρόγραμμα που να ελέγχει τη λειτουργία των κλάσεων αυτών.
Υλοποίηση με έτοιμες βιβλιοθήκες
Η βιβλιοθήκη της C++
Η βιβλιοθήκη της C++ μπορεί να χωριστεί στις παρακάτω κατηγορίες:
- Γενικές λειτουργίες
- Λειτουργίες μετατροπής και αρχείων (iostreams)
- Αλγόριθμοι με τη χρήση ενός
επαναλήπτη (iterator)
πάνω σε έναν
περιέχοντα (container).
Οι λειτουργίες αυτές αποτελούν τη βιβλιοθήκη με το όνομα
Standard Template Library (STL).
- Συμβατότητα με τη C
Οι τρεις πρώτες κατηγορίες της βιβλιοθήκης ορίζονται σε επικεφαλίδες
χωρίς το επίθεμα .h.
Για να μην υπάρχει πιθανότητα τα ονόματα που ορίζονται στις επικεφαλίδες
να ταυτίζονται με ονόματα που ορίζει ο χρήστης,
τα ονόματα που ορίζουν οι επικεφαλίδες αυτές βρίσκονται απομονωμένα
σε έναν ξεχωριστό
χώρο ονομάτων (namespace) με το πρόθεμα "std::".
Για να τα χρησιμοποιήσουμε στο πρόγραμμά μας χωρίς το πρόθεμα αυτό
μπορούμε μετά τις επικεφαλίδες να δηλώσουμε:
using namespace std;
Στις επόμενες ενότητες περιγράφουμε συνοπτικά μόνο τις γενικές
λειτουργίες της βιβλιοθήκης και με περισσότερες λεπτομέρειες τις
λειτουργίες της STL.
Γενικές λειτουργίες
Η γενικές λειτουργίες της βιβλιοθήκης της C++ είναι οι παρακάτω:
- bitset
- κλάση προτύπων (ως προς το μέγιστο πληθάριθμο του
συνόλου) που επεξεργάζεται σύνολο από bit
- complex
- κλάση προτύπων (ως προς τον τύπου των δύο στοιχείων)
μιγαδικών αριθμών.
Εξειδικεύσεις της κλάσης είναι μιγαδικοί αριθμοί float, double και
long double.
- exception
- συναρτήσεις χειρισμού διακοπών
- limits
- ιδιότητες αριθμητικών τιμών
- locale
- προσαρμογή του προγράμματος σε τοπικά χαρακτηριστικά.
Περιλαμβάνονται πρότυπες κλάσεις που ορίζουν:
- τον τρόπο μετατροπής μεταξύ χαρακτήρων
- τη σειρά ταξινόμησης
- το χαρακτηρισμό χαρακτήρων
- τη μετατροπή νομισματικών τιμών
- τη μετατροπή αριθμητικών τιμών
- τη μετατροπή τιμών που παριστάνουν χρόνο
- τη μετατροπή μηνυμάτων προς το χρήστη
- stdexcept
- κλάσεις για αναφορά διακοπών
- string
- πρότυπη κλάση που ορίζει σειρές αντικειμένων μεταβλητού
μήκους.
Εξειδίκευση της κλάσης αυτής είναι οι συμβολοσειρές.
- valarray
- πρότυπη (ως προς το περιεχόμενο του πίνακα)
κλάση που επιτρέπει το μαζικό χειρισμό πινάκων
Παράδειγμα χρήσης μιγαδικών αριθμών
Το παρακάτω παράδειγμα αθροίζει δύο μιγαδικούς αριθμούς:
#include <complex>
#include <iostream>
using namespace std;
main()
{
complex <double> a, b, c;
cin >> a >> b;
c = a + b;
cout << c << "\n";
}
Είσοδος:
(4,2)
(1,10)
Αποτέλεσμα:
(5,12)
Παράδειγμα χρήσης συμβολοσειρών
Το παρακάτω παράδειγμα αθροίζει δύο συμβολοσειρές:
#include <string>
#include <iostream>
using namespace std ;
void main()
{
string result;
string s1="hello";
string s2=", world";
cout << "s1 is " << s1 << "\n";
cout << "s2 is " << s2 << "\n";
result=s1+s2;
cout << "s1+s2 is " << result << "\n";
}
Αποτέλεσμα:
s1 is hello
s2 is , world
s1+s2 is hello, world
Λειτουργίες μετατροπής και αρχείων
Οι επικεφαλίδες iostreams υποστηρίζουν τη μετατροπή μεταξύ κειμένων
και της εσωτερική μορφή παράστασης των τύπων καθώς και είσοδο και
έξοδο από και σε εξωτερικά αρχεία.
Ορίζονται οι παρακάτω επικεφαλίδες:
- fstream
- πρότυπες κλάσεις iostreams για το χειρισμό εξωτερικών αρχείων
- iomanip
- δήλωση χειριστών iostreams
- ios
- βασική κλάση προτύπων iostreams
- iosfwd
- δήλωση προτύπων κλάσεων iostreams πριν από τον ορισμό τους
- iostream
- δήλωση των βασικών αντικειμένων iostreams
- istream
- πρότυπη κλάση που εξάγει στοιχεία
- ostream
- πρότυπη κλάση που εισάγει στοιχεία
- sstream
- πρότυπες κλάσεις iostreams που αναφέρονται σε συμβολοσειρές
- streambuf
- πρότυπες κλάσεις που προσφέρουν ενταμιευτές σε κλάσεις iostreams
- strstream
- κλάσεις iostreams που λειτουργούν σε στοιχεία που βρίσκονται στη μνήμη
Περιέχοντες και επαναλήπτες στην STL
Η STL ορίζει μια σειρά από περιέχοντες (containers) όπως την ουρά, τη στοίβα,
την απεικόνιση και τον πίνακα.
Πάνω στους περιέχοντες αυτούς επιτρέπει την εκτέλεση αλγορίθμων,
όπως την εύρεση ενός στοιχείου, η ένωση δύο περιεχόντων, η
ταξινόμηση, κ.λπ.
Στην STL
ορίζονται οι παρακάτω πρότυποι (ως προς τα στοιχεία που περιέχουν)
περιέχοντες:
- list
- διπλά συνδεδεμένη λίστα
- queue
- ουρά
- deque
- ουρά με πρόσβαση και στις δύο άκρες
- map
- απεικόνιση (π.χ. από συμβολοσειρές σε ακέραιους)
- set
- απεικόνιση με μοναδικά στοιχεία στο πεδίο τιμών
- stack
- στοίβα
- vector
- πίνακας
Η πρόσβαση των στοιχείων ενός περιέχοντα από τους αλγορίθμους
γίνεται μέσω επαναληπτών (iterators).
Οι επαναλήπτες - ανάλογα με τη δομή των δεδομένων του περιέχοντα - μπορούν
να επιτρέπουν την παρακάτω ιεραρχία κινήσεων:
- Τυχαία προσπέλαση
- Κίνηση προς τις δύο κατευθύνσεις
- Κίνηση προς τα εμπρός, ή ανάποδη κίνηση
Επίσης, μπορούν να επιτρέπουν την παρακάτω ιεραρχία πρόσβασης στα δεδομένα
που δείχνουν:
- Είσοδο και έξοδο
- Μόνο είσοδο, ή μόνο έξοδο
Η κλάση των επαναληπτών ορίζεται στην επικεφαλίδα iterator.
Μερικές μέθοδοι που ορίζονται σε επαναλήπτες είναι οι παρακάτω:
- advance
- distance
- ==, !=, <, >, >=, <=
- +, -
- ++, --
Για κάθε περιέχοντα ορίζονται μέθοδοι όπως (στην περίπτωση της
διπλής ουράς):
- assign
- ανάθεση τιμής
- at
- αναφορά σε στοιχείο
- back
- το τελευταίο στοιχείο
- begin
- επαναλήπτης που δείχνει στην αρχή της δομής
- clear
- διαγραφή όλων των στοιχείων
- empty
- αληθές αν η δομή είναι άδεια
- end
- επαναλήπτης που δείχνει στο τέλος της δομής
- erase
- διαγραφή σειράς στοιχείων
- front
- το πρώτο στοιχείο
- insert
- προσθήκη στοιχείου
- operator[]
- πρόσβαση σε στοιχείο
- pop_back
- αφαίρεση στοιχείου από το τέλος
- pop_front
- αφαίρεση στοιχείου από την αρχή
- push_back
- προσθήκη στοιχείου στο τέλος
- push_front
- προσθήκη στοιχείου στην αρχή
- rbegin
- ανάστροφος επαναλήπτης που δείχνει στην αρχή της δομής
- rend
- ανάστροφος επαναλήπτης που δείχνει στο τέλος της δομής
- resize
- καθορισμός του αριθμού των στοιχείων
- size
- αριθμός των στοιχείων
- swap
- εναλλαγή δύο στοιχείων μεταξύ τους
Παράδειγμα 1: ουρές
Το παρακάτω παράδειγμα ορίζει μια διπλή ουρά ακεραίων, προσθέτει 5 στοιχεία
και τα τυπώνει:
#include <iostream>
#include <deque>
using namespace std;
typedef deque <int> INTDEQUE;
void main()
{
// Create A and fill it with elements 1,2,3,4 and 5
// using push_back function
INTDEQUE A;
A.push_back(1);
A.push_back(2);
A.push_back(3);
A.push_back(4);
A.push_back(5);
// Print the contents of A using iterator
// and functions begin() and end()
INTDEQUE::iterator pi;
for (pi = A.begin(); pi != A.end(); pi++)
cout << *pi << " ";
cout << "\n";
}
Παράδειγμα 2: απεικόνιση
Το παρακάτω παράδειγμα τυπώνει πόσες φορές εμφανίστηκε κάθε λέξη
στην είσοδο του προγράμματος:
#include <map>
#include <iostream>
#include <string>
using namespace std;
typedef map <string, int> smap_t;
int
main()
{
string s;
smap_t m; // Our map
while (!cin.eof()) {
cin >> s;
m[s]++; // Use overloaded [] operator
};
smap_t::iterator i; // Iterator for printing the results
for (i = m.begin(); i != m.end(); i++)
cout << i->first << " " << i->second << endl;
return (0);
}
Πρόσθετες λειτουργίες στην STL
Επικεφαλίδα algorithm
Στην επικεφαλίδα algorithm ορίζονται μέθοδοι που ενεργούν πάνω
σε περιέχοντες:
- adjacent_find
- βρίσκει δύο ίσα στοιχεία σε διπλανές θέσεις
- binary_search
- δυαδική ανίχνευση
- copy
- αντιγραφή περιοχής
- copy_backward
- αντίστροφη αντιγραφή περιοχής
- count
- μέτρημα
- count_if
- μέτρημα υπό συνθήκη
- equal
- σύγκριση περιοχών
- equal_range
- σύγκριση περιοχών με συγκεκριμένη ακρίβεια
- fill
- πλήρωση περιοχής με τιμή
- fill_n
- πλήρωση αριθμού στοιχείων με τιμή
- find
- εύρεση στοιχείου
- find_end
- εύρεση στοιχείου από το τέλος
- find_first_of
- εύρεση στοιχείου ίσου με κάποιο μέλος από σύνολο στοιχείων
- find_if
- εύρεση στοιχείου που να ικανοποιεί συνθήκη
- for_each
- εκτέλεση συνάρτησης για όλα τα στοιχεία σε περιοχή
- generate
- πλήρωση περιοχής με αποτέλεσμα συνάρτησης
- generate_n
- πλήρωση αριθμού στοιχείων με αποτέλεσμα συνάρτησης
- includes
- έλεγχος αν μια περιοχή εμπεριέχει μια άλλη
- inplace_merge
- σύζευξη δεδομένων στον ίδιο περιέχοντα
- iter_swap
- εναλλαγή δύο τιμών
- lexicographical_compare
- σύγκριση δύο περιοχών α, β για α < β
- lower_bound
- εύρεση μιας ελάχιστης τιμής σε περιοχή σε σχέση με μιαν άλλη τιμή
- make_heap
- μετατροπή περιοχής σε
σωρό (heap)
(δυαδικό δένδρο στο οποίο τα παιδιά έχουν
τιμή μικρότερη ή ίση από αυτή των γονέων τους).
- max
- το μέγιστο από δύο στοιχεία
- max_element
- εύρεση του μέγιστου στοιχείου σε περιοχή
- merge
- σύζευξη δύο περιοχών σε τρίτη
- min
- το ελάχιστο από δύο στοιχεία
- min_element
- εύρεση του ελαχίστου στοιχείου σε περιοχή
- mismatch
- εύρεση του πρώτου διαφορετικού στοιχείου ανάμεσα σε δύο περιοχές
- next_permutation
- υπολογισμός της επόμενης μετάθεσης σε μια περιοχή
- nth_element
- θέτει ένα στοιχείο στη θέση που θα έπρεπε να έχει αν η
περιοχή ήταν ταξινομημένη
- partial_sort
- ταξινομεί τα πρώτα στοιχεία μιας περιοχής
- partial_sort_copy
- ταξινομεί τα πρώτα στοιχεία μιας περιοχής σε μιαν άλλη
- partition
-
χωρίζει μια περιοχή στα δύο με βάση μια συνάρτηση και επιστρέφει
το σημείο που είναι ο χωρισμός
- pop_heap
- αφαίρεση στοιχείου από σωρό
- prev_permutation
- υπολογισμός της προηγούμενης μετάθεσης σε μια περιοχή
- push_heap
- προσθήκη στοιχείου από σωρό
- random_shuffle
- ανακατεύει μια περιοχή
- remove
- αφαιρεί στοιχεία ίσα με μια τιμή
- remove_copy
- αφαιρεί στοιχεία ίσα με μια τιμή μεταφέροντας
το αποτέλεσμα σε μιαν άλλη περιοχή
- remove_copy_if
- αφαιρεί στοιχεία για τα οποία μια συνάρτηση είναι
αληθής μεταφέροντας το αποτέλεσμα σε μιαν άλλη περιοχή
- remove_if
- αφαιρεί στοιχεία για τα οποία μια συνάρτηση είναι αληθής
- replace
- αλλάζει τιμή σε στοιχεία ίσα με μια τιμή
- replace_copy
- αλλάζει τιμή σε στοιχεία ίσα με μια τιμή μεταφέροντας
το αποτέλεσμα σε μιαν άλλη περιοχή
- replace_copy_if
- αλλάζει τιμή σε στοιχεία για τα οποία μια συνάρτηση είναι
αληθής μεταφέροντας το αποτέλεσμα σε μιαν άλλη περιοχή
- replace_if
- αλλάζει τιμή σε στοιχεία για τα οποία μια συνάρτηση είναι αληθής
- reverse
- αντιστρέφει τη σειρά σε μια περιοχή
- reverse_copy
- αντιστρέφει τη σειρά σε μια περιοχή
μεταφέροντάς την σε μιαν άλλη περιοχή
- rotate
- περιστρέφει τη σειρά των στοιχείων σε μια περιοχή
- rotate_copy
- περιστρέφει τη σειρά των στοιχείων σε μια περιοχή
μεταφέροντάς την σε μιαν άλλη περιοχή
- search
- εύρεση σειράς στοιχείων σε μια περιοχή ίσης με στοιχεία μιας άλλης
- search_n
- εύρεση σειράς στοιχείων σε μια περιοχή ίσης με αριθμό στοιχείων μιας άλλης
- set_difference
- θέτει μια περιοχή ίση με τη διαφορά των στοιχείων δύο
άλλων περιοχών (διαφορά συνόλων)
- set_intersection
- θέτει μια περιοχή ίση με την τομή των στοιχείων δύο άλλων περιοχών (τομή συνόλων)
- set_symmetric_difference
- θέτει μια περιοχή ίση με τα μη κοινά των στοιχείων δύο άλλων περιοχών
- set_union
- θέτει μια περιοχή ίση με την ένωση των στοιχείων δύο άλλων περιοχών (ένωση συνόλων)
- sort
- ταξινομεί μια περιοχή
- sort_heap
- ταξινομεί έναν σωρό
- stable_partition
-
χωρίζει μια περιοχή στα δύο με βάση μια συνάρτηση και επιστρέφει
το σημείο που είναι ο χωρισμός.
Ο χωρισμός γίνεται χωρίς να αλλάξει η
σχετική σειρά των στοιχείων.
- stable_sort
- ταξινομεί μια περιοχή.
Η ταξινόμηση γίνεται χωρίς να αλλάξει η
σχετική σειρά των στοιχείων που είναι μεταξύ τους ίσα.
- swap
- αντιστρέφει μεταξύ τους δύο στοιχεία
- swap_ranges
- αντιστρέφει μεταξύ τους δύο περιοχές
- transform
- εφαρμόζει έναν τελεστή σε μια περιοχή ή μεταξύ δύο
περιοχών
- unique
- αφαιρεί τα όμοια στοιχεία από μια περιοχή
- unique_copy
- αφαιρεί τα όμοια στοιχεία από μια περιοχή
μεταφέροντάς την σε μιαν άλλη περιοχή
- upper_bound
- εύρεση μιας μέγιστης τιμής σε περιοχή σε σχέση με μια άλλη τιμή
Επικεφαλίδα numeric
Στην επικεφαλίδα algorithm ορίζονται αριθμητικές μέθοδοι που ενεργούν πάνω
σε περιέχοντες:
- accumulate
- υπολογίζει ένα σύνολο πάνω σε μια περιοχή
- adjacent_difference
- υπολογίζει τις διαφορές τιμών μεταξύ στοιχείων
μιας περιοχής
- inner_product
- υπολογίζει ένα εσωτερικό γινόμενο μεταξύ δύο
περιοχών
- partial_sum
- υπολογίζει ένα μερικό άθροισμα τιμών μιας
περιοχής σε μιαν άλλη
Άλλες επικεφαλίδες
Ακόμα στην STL ορίζονται οι παρακάτω επικεφαλίδες:
- utility
- πρότυπη κλάση που ορίζει διάταξη σε ζεύγη τιμών
- functional
- κλάση που επιτρέπει συναρτησιακό προγραμματισμό
- memory
- ορίζει την κλάση allocator η οποία κατανέμει τη μνήμη
σε όλους τους περιέχοντες.
Ο επανακαθορισμός της επιτρέπει την υλοποίηση άλλων στρατηγικών
καταμερισμού και πρόσβασης στη μνήμη.
Συμβατότητα με τη C
Ακόμα παρέχονται επικεφαλίδες που παρέχουν υπηρεσίες
όμοιες με τις αντίστοιχες της γλώσσας C.
Καλό είναι οι επικεφαλίδες αυτές να χρησιμοποιούνται μόνο όταν
απαιτείται συμβατότητα με κληρονομημένο (legacy)
(παλιό) κώδικα.
- cassert
- απόδειξη ιδιοτήτων κατά την εκτέλεση συναρτήσεων
- cctype
- χαρακτηρισμός χαρακτήρων
- cerrno
- πρόσβαση στα λάθη από την εκτέλεση συναρτήσεων της βιβλιοθήκης
- cfloat
- ιδιότητες αριθμών κινητής υποδιαστολής
- ciso646
- ορισμός λέξεων που επιτρέπουν τον προγραμματισμό σε συστήματα με
τις κωδικοσελίδες ISO 646 (δεν αφορά την Ελλάδα)
- climits
- ιδιότητες των ακεραίων
- clocale
- προσαρμογή του προγράμματος σε τοπικά χαρακτηριστικά
- cmath
- μαθηματικές συναρτήσεις
- csetjmp
- αδόμητη μετάβαση ελέγχου μεταξύ συναρτήσεων
- csignal
- έλεγχος διακοπών
- cstdarg
- πρόσβαση σε ορίσματα με μεταβλητό αριθμό παραμέτρων
- cstddef
- ορισμός χρησίμων τύπων
- cstdio
- είσοδος και έξοδος όμοια με τη C
- cstdlib
- πρόσβαση στις αντίστοιχες συναρτήσεις της C
- cstring
- επεξεργασία συμβολοσειρών της C
- ctime
- χρήση ώρας και ημερομηνίας
- cwchar
- επεξεργασία "πλατιών" χαρακτήρων
- cwctype
- χαρακτηρισμός "πλατιών" χαρακτήρων
Βιβλιογραφία
- Matthew H. Austern.
Generic Programming and the STL: Using and Extending the C++ Standard
Template Library.
Addison-Wesley, 1998.
Ασκήσεις
Άσκηση 7
- Με βάση τον περιέχοντα της STL queue και μονοτονικά αυξανόμενη μεταβλητή
να υλοποιηθεί πρόγραμμα το οποίο να υλοποιεί ουρά εξυπηρέτησης πελατών
ως εξής:
- Όταν εισάγεται ο χαρακτήρας I (In) το πρόγραμμα τυπώνει τον
αριθμό προτεραιότητας του νέου πελάτη.
- Όταν εισάγεται ο χαρακτήρας O (Out) το πρόγραμμα τυπώνει τον
αριθμό προτεραιότητας του επόμενου πελάτη που θα εξυπηρετηθεί.
Παράδειγμα:
I
1
I
2
I
3
O
1
I
4
O
2
...
Υλοποίηση διεπαφών σε Visual Basic
Παράθυρα, εικονίδια, μενού, και δείκτης
Στο σύγχρονο περιβάλλον διεπαφής με το χρήστη βασίζεται
στα παράθυρα (windows),
τα εικονίδια (icons),
τα μενού (menus) και
το δείκτη (pointer).
Το περιβάλλον αυτό σχεδιάστηκε για πρώτη φορά στο Palo Alto Research Center
της Xerox (PARC) και υλοποιήθηκε με επιτυχία από την Apple για τους υπολογιστές
Macintosh και από τη Micosoft στην οικογένεια Windows.
Η επιφάνεια εργασίας χρησιμοποιεί ως βάση τη
μεταφορά του γραφείου (desktop metaphor).
Ο χρήστης μετακινεί πάνω στην οθόνη παράθυρα με τη χρήση του δείκτη
όπως θα κινούσε έγγραφα στο γραφείο με τα χέρια του.
Βασικά τεχνολογικά στοιχεία για τη λειτουργία του περιβάλλοντος αυτού
είναι η οθόνη χαρτογραφικής απεικόνισης (bitmap display)
και το ποντίκι (mouse) ή κάποιος άλλος αντίστοιχος μηχανισμός
που επιτρέπει στο χρήστη να δείχνει αντικείμενα στην οθόνη.
Με τη χρήση εικονιδίων ορισμένα στοιχεία του περιβάλλοντος μπορούν να
παρασταθούν με αμεσότητα, ενώ τα μενού κάνουν τις λειτουργίες του περιβάλλοντος
προσιτές χωρίς να χρειάζεται ο χρήστης να απομνημονεύει εντολές και τη σύνταξή
τους.
Εικονίδια στο περιβάλλον Windows
Εισαγωγή εικόνας στο κείμενο με τη χρήση μενού στο πρόγραμμα Microsoft Word
<img src="./vb/inspic.gif">
Εισαγωγή εικόνας στο κείμενο στη γλώσσα HTML
Εσωτερική δομή
Εσωτερικά τα συστήματα γραφικής διεπαφής που βασίζονται σε παράθυρα
λειτουργούν με βάση γεγονότα (events) τα οποία
στέλνονται στις εφαρμογές.
Το κάθε παράθυρο είναι μια (τυπικά ορθογώνια)
περιοχή της οθόνης η οποία σχεδιάζεται αυτόνομα
και λαμβάνει γεγονότα.
Κάθε εφαρμογή μπορεί να απαρτίζεται από ένα ή περισσότερα παράθυρα.
Γεγονότα μπορεί να είναι π.χ. η επιλογή μιας εντολής από το μενού,
η αλλαγή του κειμένου που γράφει ο χρήστης,
το πάτημα ενός πλήκτρου,
η κίνηση του ποντικιού ή,
η αλλαγή της ώρας.
Τα γεγονότα μπορεί να δημιουργούνται άμεσα από το χρήστη ή να συνθέτονται
από το σύστημα ως αποτέλεσμα εντολών του χρήστη, ή άλλων εξωτερικών διεπαφών.
Το κάθε γεγονός στέλνεται σε μια ή περισσότερες εφαρμογές.
Οι εφαρμογές με τη σειρά τους στέλνουν τα γεγονότα στα παράθυρα που
μπορούν να τα επεξεργαστούν.
Έτσι, αντί η εφαρμογή να ζητά στοιχεία από το χρήστη, τα στοιχεία από το χρήστη
στέλνονται ως γεγονότα στην εφαρμογή.
Στο περιβάλλον των Windows ορίζονται πάνω από 800 διαφορετικά γεγονότα.
Για την επιβολή συνέπειας στη διεπαφή με το χρήστη αλλά και
τη διευκόλυνση υλοποίησης των εφαρμογών τα παραθυρικά περιβάλλοντα ορίζουν
μια σειρά από όργανα (controls) (ή widgets) τα
οποία ενσωματώνουν και διαθέτουν ένα σύνολο από λειτουργίες που είναι
συχνά χρήσιμες.
Παραδείγματα τέτοιων οργάνων είναι
το πλαίσιο εισαγωγής κειμένου (edit box),
το πιεζόμενο κουμπί (push button) και
το πλαίσιο επιλογής αρχείου.
Η μεταφορά των γεγονότων από το σύστημα στις εφαρμογές και τα παράθυρά
τους βασίζεται συχνά στην κληρονομικότητα.
Έτσι, μια εφαρμογή μπορεί να δημιουργήσει υποκλάσεις για ένα όργανο που
να δίνουν στο όργανο πιο εξειδικευμένη λειτουργικότητα.
Αντίστοιχα, τα μηνύματα από το σύστημα κατεβαίνουν το δέντρο της
κληρονομικότητας μιας εφαρμογής μέχρι και το τελευταίο στην ιεραρχία
παράθυρο ή όργανο να δει το γεγονός και να έχει την ευκαιρία να το
χειριστεί.
Μια εφαρμογή πρέπει να παραλαμβάνει συνέχεια όλα τα μηνύματα που στέλνονται
σε αυτή, να τα διαχωρίζει και να τα προωθεί στους αντίστοιχους παραλήπτες.
Ο παρακάτω κώδικας αποτελεί τυπικό παράδειγμα του βρόχου που παραλαμβάνει
και προωθεί μηνύματα σε εφαρμογές Windows:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
if (!IsDialogMessage(hWnd, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Αντίστοιχα ο παρακάτω κώδικας αποτελεί ένα απλό παράδειγμα για το πως
μπορεί να γίνει απλοϊκά η επεξεργασία των μηνυμάτων:
long
WdInWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
static BOOL fFirstPaint;
static HWND hWndList;
static HWND hWndEdit;
HWND focus;
int i;
switch (iMessage) {
case WM_CREATE:
fFirstPaint = TRUE;
break;
case WM_PAINT:
if (fFirstPaint) {
fFirstPaint = FALSE;
/* Set focus to the input string box */
hWndEdit = GetDlgItem(hDlgWnd, ID_STRINGEDIT);
hWndList = GetDlgItem(hDlgWnd, ID_LISTBOX);
/* Blank all lines so that we start from the bottom */
for (i = 0; i < HLEN; i++)
(void)SendMessage(hWndList, LB_INSERTSTRING,
(WPARAM)-1, (LPARAM)(LPCSTR)"");
SetFocus(hWndEdit);
}
return (DefWindowProc(hWnd, iMessage, wParam, lParam));
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_COMMAND:
switch (wParam) {
case IDOK: /* Enter key */
focus = GetFocus();
if (focus == hWndEdit)
newline(hWndEdit, hWndList);
else if (focus == hWndList)
histline(hWndList, hWndEdit);
else
goto aka_default;
break;
case ID_LISTBOX:
if (HIWORD(lParam) == LBN_DBLCLK)
histline(hWndList, hWndEdit);
else
goto aka_default;
break;
default:
goto aka_default;
}
break;
default:
aka_default:
return (DefWindowProc(hWnd, iMessage, wParam, lParam));
}
return 0L;
}
Είναι φανερό πως το παραπάνω μοντέλο δε μπορεί να εφαρμοστεί αποτελεσματικά
σε εφαρμογές με εκατοντάδες εντολές και αντίστοιχα μηνύματα.
Απεικόνιση της δομής στη Visual Basic
Το περιβάλλον προγραμματισμού Visual Basic επιτρέπει τη γραφική σχεδίαση
εφαρμογών με τη χρήση οργάνων και τον άμεσο ορισμό κώδικα που θα αποκρίνεται
σε συγκεκριμένα γεγονότα.
Για το σκοπό αυτό η Visual Basic απλοποιεί το περιβάλλον προγραμματισμού
που ορίζουν τα Windows δίνοντας στο χρήστη μια σειρά από εξειδικευμένα
όργανα (βασισμένα σε αυτά των Windows) και έναν τρόπο να ορίζει ο χρήστης
την εμφάνιση του κάθε οργάνου, τις λειτουργίες του και τον
τρόπο που αυτό ανταποκρίνεται σε γεγονότα.
Έτσι, κάθε όργανο παριστάνεται ως ένα αντικείμενο που διαθέτει:
- ιδιότητες όπως για παράδειγμα τη θέση και το χρώμα του,
- μεθόδους που μπορούν να κληθούν για εκτέλεση όπως για
παράδειγμα τη μέθοδο της επανασχεδίασης και
- γεγονότα στα οποία μπορεί ο χρήστης να συνδέσει κώδικα
π.χ. το γεγονός click.
Ο έλεγχος που έχει ο χρήστης πάνω στα όργανα διαφέρει ανάλογα με
την κατάσταση της εφαρμογής:
-
Κατά τη διάρκεια σχεδιασμού της εφαρμογής, ο χρήστης μπορεί να διαβάσει
και να αλλάξει τιμή στις περισσότερες ιδιότητες καθώς και να συνδέσει
κώδικα με όλα τα γεγονότα κάθε οργάνου.
-
Κατά τη διάρκεια εκτέλεσης της εφαρμογής, το πρόγραμμα μπορεί να διαβάσει
και να αλλάξει τιμή στις περισσότερες ιδιότητες και να καλέσει τις
μεθόδους του κάθε οργάνου.
Όταν παραλαμβάνεται από την εφαρμογή ένα γεγονός, αυτό προωθείται
αυτόματα στον κώδικα που έχει συνδεθεί με το αντίστοιχο όργανο.
Αν δεν υπάρχει τέτοιος κώδικας, τότε η εφαρμογή εκτελεί αυτόματα
κώδικα ο οποίος εκτελεί τη βασική λειτουργία για το αντίστοιχο γεγονός.
Όργανα
Όργανα τοποθετούνται μέσα στην εφαρμογή με γραφικό τρόπο κατά την
υλοποίησή της.
Κάθε όργανο διακρίνεται από το όνομά του.
Αν πολλά όργανα ίδιου τύπου έχουν το ίδιο όνομα τότε αυτά ορίζουν
έναν πίνακα οργάνων τον οποίο μπορούμε να διατρέξουμε με τη χρήση
ενός δείκτη (π.χ. txtTextBox(i).text = "hello").
Το περιβάλλον της Visual Basic ορίζει τα παρακάτω βασικά
όργανα:
- TextBox
-
Όργανο για είσοδο κειμένου
- Label
-
Εμφάνιση κειμένου
- PictureBox
-
Περιοχή για εμφάνιση και σχεδίαση γραφικών κατά τη διάρκεια εκτέλεσης
του προγράμματος.
- CheckBox
-
Επιλογή τύπου ναι/όχι (μπορούν να είναι αληθείς πολλαπλές τέτοιες επιλογές)
- OptionButton
-
Επιλογή τύπου ναι/όχι (δεν μπορούν να είναι αληθείς πολλαπλές τέτοιες επιλογές)
- CommandButton
-
Κουμπί για εντολές
- Image
-
Χαρτογραφική εικόνα που μπορεί να ορίσει ο χρήστης
- Shape
-
Σχήμα που μπορεί να οριστεί κατά τη σχεδίαση της εφαρμογής.
- Timer
-
Χρονόμετρο που δημιουργεί γεγονότα σε τακτά διαστήματα.
- ListBox
-
Λίστα με επιλογές.
- ComboBox
-
Λίστα με επιλογές σε συνδυασμό με περιοχή που μπορεί να γραφτεί κείμενο
- VScrollBar
-
Κάθετο όργανο ελέγχου της θέσης στο παράθυρο.
- HScrollBar
-
Οριζόντιο όργανο ελέγχου της θέσης στο παράθυρο.
- Frame
-
Περιοχή στην οποία μπορούν να ομαδοποιηθούν πολλά όργανα (ειδικά
OptionButton)
Εκτός από τα παραπάνω βασικά όργανα μπορεί κανείς να ορίσει ή να αγοράσει
πολλά άλλα όργανα που εκτελούν χρήσιμες λειτουργίες όπως π.χ. σύνδεση
με βάσεις δεδομένων και το Internet, εμφάνιση γραφημάτων και πινάκων.
Τα όργανα αυτά μπορούν να θεωρηθούν εξαρτήματα (components)
για την υλοποίηση εφαρμογών.
Γεγονότα
Κάθε όργανο έχει το δικό του σύνολο από γεγονότα τα οποία μπορεί να δεχτεί.
Ορισμένα από τα γεγονότα τα οποία αφορούν πολλά όργανα είναι τα παρακάτω:
- Click
- Click του ποντικιού πάνω στο όργανο.
- DblClick
- Διπλό click του ποντικιού πάνω στο όργανο.
- GotFocus
- Το όργανο γίνεται η εστία εισόδου.
- LostFocus
- Το όργανο παύει να είναι η εστία εισόδου.
- KeyDown
- Ένα πλήκτρο πατιέται πάνω από το όργανο.
- KeyUp
- Ένα πλήκτρο αφήνεται πάνω από το όργανο.
- KeyPress
- Ένας χαρακτήρας γράφεται πάνω από το όργανο.
- MouseDown
- Ένα πλήκτρο του ποντικιού πατιέται πάνω από το όργανο.
- MouseUp
- Ένα πλήκτρο του ποντικιού αφήνεται πάνω από το όργανο.
- MouseMove
- Το ποντίκι κινείται πάνω από το όργανο.
- Change
- Τα δεδομένα που περιέχει το όργανο άλλαξαν.
Η σύνδεση του γεγονότος με κώδικα της Visual Basic γίνεται με τον
ορισμό μιας συνάρτησης που έχει ως όρισμα παραμέτρους που αφορούν το
συγκεκριμένο όργανο.
Παράδειγμα:
Private Sub Check1_KeyPress(KeyAscii As Integer)
End Sub
Private Sub Check1_KeyUp(KeyCode As Integer, Shift As Integer)
End Sub
Private Sub Check1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
End Sub
Private Sub Command1_Click()
End Sub
Private Sub Text1_Change()
End Sub
Ιδιότητες
Οι ιδιότητες που αφορούν κάθε όργανο μπορούν να μεταβληθούν τόσο κατά το
σχεδιασμό, όσο και κατά την εκτέλεση της εφαρμογής.
Πρόσβαση στις ιδιότητες κατά την εκτέλεση της εφαρμογής έχουμε με τη
σύνταξη όργανο.ιδιότητα.
Μερικές ιδιότητες που εμφανίζονται σε πολλά όργανα είναι οι παρακάτω:
- Name
- Το όνομα του συγκεκριμένου οργάνου.
- BackColor
- Το χρώμα του φόντου.
- ForeColor
- Το χρώμα σχεδίασης.
- Enabled
- Αληθές αν το όργανο είναι ενεργό.
- Visible
- Αληθές αν το όργανο είναι ορατό.
- Text
- Το κείμενο που περιέχει το όργανο.
- Caption
- Το κείμενο που εμφανίζει το όργανο.
- Value
- Η τιμή που έχει το όργανο (π.χ. αληθές/ψευδές).
- ToolTipText
- Το αναδυόμενο κείμενο σύντομης βοήθειας.
- Height
- Το ύψος του οργάνου.
- Left
- Η θέση του οργάνου στη φόρμα από αριστερά.
- Top
- Η θέση του οργάνου στη φόρμα από πάνω.
- Width
- Το πλάτος του οργάνου.
Ολοκληρωμένο περιβάλλον ανάπτυξης
Μια εφαρμογή Visual Basic υλοποιείται με βάση διάφορες
φόρμες (forms) που ορίζουν τα περιεχόμενα για
αντίστοιχα παράθυρα.
Κάθε φόρμα έχει, όπως και τα όργανα, ιδιότητες και μεθόδους.
Το περιβάλλον υλοποίησης της Visual Basic έχει την παρακάτω μορφή:
- Στο κέντρο σχεδιάζεται η φόρμα της εφαρμογής
- Αριστερά επιλέγονται τα όργανα που τοποθετούνται στη φόρμα.
- Άνω δεξιά μπορεί κανείς να επιλέξει τις φόρμες της εφαρμογής καθώς
και να αλλάξει μεταξύ γραφικής εμφάνισης και της εμφάνισης του κώδικα για
τα όργανα.
- Κάτω δεξιά μπορεί κανείς να διαβάσει και να αλλάξει τις ιδιότητες
για το κάθε φορά επιλεγμένο όργανο.
- Με την πάνω γραμμή εργαλείων μπορεί κανείς μεταξύ άλλων να
ξεκινήσει και να σταματήσει την εφαρμογή, να προσθέσει φόρμες και να
ορίσει τα μενού της εφαρμογής.
Για να ορίσουμε τον κώδικα που εκτελείται για κάποιο γεγονός κάνουμε
διπλό κλικ σε κάποιο όργανο και αυτόματα δημιουργείται η δήλωση για
τη συνάρτηση που αφορά το πιο χρήσιμο γεγονός για το αντίστοιχο όργανο.
Από επιλογές στην κορυφή του παραθύρου μπορούμε να επιλέξουμε το όργανο και
το γεγονός για τα οποία θέλουμε να γράψουμε κώδικα:
Η γλώσσα Visual Basic
Η γλώσσα Visual Basic είναι μια δομημένη γλώσσα με τύπους
που υποστηρίζει, μερικώς, τον προγραμματισμό με αντικείμενα
(δεν υποστηρίζει κληρονομικότητα).
Στις επόμενες παραγράφους περιγράφουμε πολύ συνοπτικά ορισμένα
βασικά στοιχεία της:
Ορισμός συναρτήσεων
function funcname(param1 as type1, param2 as type2) as returntype
end function
Παράδειγμα:
function square(x as double) as double
square = x * x
end function
Διαδικασίες (συναρτήσεις που δεν επιστρέφουν τιμή) ορίζονται αντίστοιχα,
αλλά με τη λέξη sub αντί για function.
Τύποι
Ορισμένοι βασικοί τύποι είναι οι boolean, integer, single, double, string.
Ορισμός μεταβλητών
Μεταβλητές ορίζονται με τη σύνταξη:
dim varname as type
Παράδειγμα:
dim x as double
dim k as integer
Εντολές
Οι εντολές τερματίζονται από το τέλος της γραμμής
Παράδειγμα:
x = 3
txt.Text = "Hello"
Σχόλια
Τα σχόλια αρχίζουν με το χαρακτήρα '
Δομές ελέγχου
Η Visual Basic παρέχει τις παρακάτω δομές ελέγχου (σε σχόλιο
η αντίστοιχη δομή της C).
' if (...) { ... } else { ... }
if boolean then
statement
...
else
statement
...
end if
' for (v = start; v <= end; v++) { ... }
for variable = start to end
statement
...
next variable
' while (...) { ... }
do while boolean
statement
...
loop
' do { ... } while (...)
do
statement
..
loop while boolean
' switch (c) { case ... default: }
Select Case testexpression
Case expressionlist
statement
...
Case Else
statement
...
End Select
Βιβλιογραφία
-
Microsoft Developer Network Library
Visual Studio Documentation -
Visual Basic -
Reference -
Controls Reference -
Intrinsic Controls
- Michael Halvorson
Visual Basic 6 Βήμα προς βήμα. Εκδόσεις Κλειδάριθμος, Αθήνα.
- Stuart K. Card, Thomas P.
Moran, and Allen Newell.
The
Psychology of Human-Computer Interaction.
Lawrence Erlbaum Associates, Publishers, New Jersey, USA, 1983.
- Adele Goldberg.
Smalltalk 80: The Language and its Implementation.
Addison Wesley, 1980.
- Brian W. Kernighan.
Experience with Tcl slash Tk for scientific and
engineering visualization.
In Proceedings of the Tcl slash Tk Workshop, pages 269–278,
Toronto, Ontario, Canada, July 1995.
- Microsoft Corp.
Windows Interface Guidelines for Software Design.
Microsoft Press, Redmond, WA, USA, 1995.
- Donald A. Norman.
The
Psychology of Everyday Things.
BasicBooks, New York, NY, USA, 1988.
- Donald A. Norman.
The
Invisible Computer.
MIT Press, 1998.
- John K. Ousterhout.
Tcl
and the Tk Toolkit.
Addison-Wesley, 1994.
- Diomidis Spinellis.
A critique of the Windows application programming interface (http://kerkis.math.aegean.gr/~dspin/pubs/jrnl/1997-CSI-WinApi/html/win.html).
Computer Standards & Interfaces, 20:1–8, November 1998.
- J. J. Thomas and
G. Hamlin.
Graphical input interaction technique workshop summary.
Computer Graphics, pages 5–30, January 1983.
- Edward R. Tufte.
The Visual Display of Quantitative Information.
Graphics Press, 1983.
Ασκήσεις
Άσκηση 7
- Να προδιαγράψετε ένα χρήσιμο και πρωτότυπο πρόγραμμα που
να μπορείτε να υλοποιήσετε σε Visual Basic.
Το πρόγραμμα πρέπει να χρησιμοποιεί τις δυνατότητες του περιβάλλοντος
Windows και να είναι φιλικό προς το χρήστη.
Η προδιαγραφή πρέπει να είναι πλήρης και δομημένη
(μπορεί π.χ. να ακολουθεί τη δομή που έχει διδαχτεί στο μάθημα
σχεδιασμός και υλοποίηση λογισμικού).
- Με βάση την προδιαγραφή αυτή να υλοποιήσετε το αντίστοιχο πρόγραμμα.
- Με την άσκηση θα παραδώσετε εκτυπωμένα την προδιαγραφή καθώς και τις
βασικές οθόνες του προγράμματος.
Σημείωση: Μπορείτε να μεταφέρετε την εικόνα από ένα παράθυρο στο clipboard,
για να την επικολλήσετε στον επεξεργαστή κειμένου,
με το συνδυασμό πλήκτρων ALT-PrtSc.
Ολοκληρωμένα περιβάλλοντα ανάπτυξης
Εισαγωγή
- Η διαδικασία της κωδικοποίησης είναι περίπλοκη και χρονοβόρα.
- Για τη διευκόλυνσή της έχουν σχεδιαστεί και αναπτυχθεί
περιβάλλοντα υποστήριξης της κωδικοποίησης (programming support environments).
- Σε ένα περιβάλλον υποστήριξης της κωδικοποίησης όλος ο κύκλος της
κωδικοποίησης από την αρχική εισαγωγή του προγράμματος μέχρι την τελική
αποσφαλμάτωση υποστηρίζεται από ένα ολοκληρωμένο σύστημα.
Έτσι ένα περιβάλλον υποστήριξης της κωδικοποίησης μπορεί να περιέχει
σε ένα ολοκληρωμένο περιβάλλον:
- Υπερσύνολο του περιβάλλοντος υποστήριξης της κωδικοποίησης
αποτελεί το
περιβάλλον τεχνολογίας λογισμικού (software engineering environment)
το οποίο παρέχει υποστήριξη για όλον το κύκλο ζωής του λογισμικού
συμπεριλαμβάνοντας δηλαδή υποστήριξη για
- την ανάλυση των απαιτήσεων,
- το σχεδιασμό,
- την κωδικοποίηση,
- τη διοίκηση του έργου,
- τον έλεγχο των εκδόσεων και του σχηματισμού,
- τη διαχείριση εξαρτημάτων λογισμικού (software components),
- τη διασφάλιση ποιότητας και
- τη συντήρηση.
- Η τάση που εμφανίζεται είναι τα περιβάλλοντα υποστήριξης της κωδικοποίησης
τα περιέχουν όλο και περισσότερα στοιχεία από περιβάλλοντα τεχνολογίας λογισμικού
ενώ παράλληλα περιβάλλοντα τεχνολογίας λογισμικού να υποστηρίζουν όλο
και περισσότερο την κωδικοποίηση με τη χρήση γεννητριών κώδικα.
Ο διορθωτής
Ο διορθωτής σε ένα ολοκληρωμένο περιβάλλον ανάπτυξης περιλαμβάνει
ισχυρές δυνατότητες εισαγωγής του κώδικα καθώς και δυνατότητες που
σχετίζονται με τη γλώσσα προγραμματισμού όπως:
- αυτόματη στοίχιση,
- συντακτικό χρωματισμό του κειμένου,
- εμφάνιση του ορίσματος των συναρτήσεων,
- συντακτικό έλεγχο,
- αναίρεση πολλαπλών επιπέδων,
- σελιδοδείκτες,
- σύνδεση με το σύστημα τεκμηρίωσης,
- ταυτόχρονη διόρθωση πολλών αρχείων,
- εύρεση κειμένου σε αρχεία,
- αυτόματη μορφοποίηση του προγράμματος
Σε περιβάλλοντα που στοχεύουν στην ανάπτυξη προγραμμάτων με γραφικό σύστημα
διεπαφής με το χρήστη συχνά υπάρχει και διορθωτής ο οποίος επιτρέπει
το σχεδιασμό του συστήματος διεπαφής.
Ο διορθωτής αυτό επιτρέπει την εισαγωγή στοιχείων του γραφικού περιβάλλοντος.
Μερικά στοιχεία που απαντώνται συχνά είναι:
Το παρακάτω σχήμα δείχνει ένα πλαίσιο διαλόγου σχεδιασμένο μέσα στο περιβάλλον
του διορθωτή.
Το σύστημα βοήθειας
Το σύστημα βοήθειας περιλαμβάνει συχνά σε ψηφιακή μορφή τεκμηρίωση για:
- τη γλώσσα προγραμματισμού,
- τη βιβλιοθήκη,
- το περιβάλλον ανάπτυξης και τα εργαλεία που το απαρτίζουν,
- τεκμηρίωση για τη διαδικασία ανάπτυξης εφαρμογών,
- οδηγίες συμβατότητας,
- απαντήσεις σε συχνές ερωτήσεις,
- διαπιστωμένα σφάλματα του περιβάλλοντος.
Το σύστημα βοήθειας είναι τις περισσότερες φορές παρουσιασμένο σε
μορφή υπερκειμένου με πίνακες περιεχομένων και συνδέσεις ανάμεσα σε
τμήματα, όπως φαίνεται στο παρακάτω σχήμα:
Μερικές φορές το σύστημα βοηθείας συμπληρώνεται από
οδηγούς (wizards) που επιτρέπουν με διαλογικό
τρόπο τη βήμα με βήμα ανάπτυξη μιας εφαρμογής.
Τα παρακάτω σχήμα παριστάνει ένα στάδιο από την εκτέλεση ενός
οδηγού:
Συχνά υπάρχει άμεση σύνδεση του διορθωτή με το σύστημα βοήθειας έτσι
ώστε την ώρα που π.χ. πληκτρολογούμε την κλήση σε μια συνάρτηση της
βιβλιοθήκης να μπορούμε να δούμε τον ορισμό της.
Η διαδικασία μεταγλώττισης
Η διαδικασία της μεταγλώττισης περιλαμβάνει αρκετές ευκολίες σε ένα
ολοκληρωμένο περιβάλλον.
- Υπολογίζονται αυτόματα τα αρχεία που απαιτούν μεταγλώττιση ανάλογα
με τις αλλαγές που έγιναν σε αρχεία επικεφαλίδων.
- Η εμφάνιση ενός λάθους επιτρέπει την άμεση σύνδεσή του με τη γραμμή
του κώδικα που το δημιούργησε όπως φαίνεται στην παρακάτω οθόνη:
- Επιτρέπει την εμφάνιση τεκμηρίωσης σχετικά με τα λάθη που εμφανίζονται.
Αποσφαλμάτωση
Ο αποσφαλματωτής επιτρέπει τον πλήρη έλεγχο της ροής εκτέλεσης
και των δεδομένων του προγράμματος που εκτελείται.
Περιλαμβάνει δυνατότητες όπως:
- τη γραμμή προς γραμμή εκτέλεση του προγράμματος,
- την εμφάνιση της τιμής των μεταβλητών,
- τη διακοπή της εκτέλεσης του προγράμματος σε μια συγκεκριμένη γραμμή,
- τη διακοπή της εκτέλεσης του προγράμματος όταν αλλάξει τιμή μια μεταβλητή,
- την εκτέλεση μια ολόκληρης συνάρτησης,
- την εμφάνιση της τιμής μιας έκφρασης,
- την εκτέλεση του προγράμματος μέχρι ένα ορισμένο σημείο,
- την εμφάνιση της στοίβας κλήσεων (call stack) των
συναρτήσεων,
- την εμφάνιση των καταχωρητών και της μνήμης του επεξεργαστή,
- την εμφάνιση του πηγαίου κώδικα καθώς και του συμβολικού κώδικα,
- τη δυναμική αλλαγή του πηγαίου κώδικα κατά τη διάρκεια της εκτέλεσης.
To παρακάτω σχήμα εμφανίζει ένα πρόγραμμα που εκτελείται σε περιβάλλον
αποσφαλματωτή.
Φυλλομέτρηση πηγαίου κώδικα και κλάσεων
Συνδεδεμένο με το διορθωτή είναι συχνά ένα εργαλείο που επιτρέπει τη
φυλλομέτρηση και την εμφάνιση της δομής του κώδικα και των κλάσεων
που τον απαρτίζουν.
Δυνατότητες του φυλλομετρητή μπορεί να είναι οι παρακάτω:
Βιβλιογραφία
- Εμμανουήλ Σκορδαλάκης.
Εισαγωγή στην Τεχνολογία Λογισμικού σ. 241-295.
Συμμετρία, 1991.
Χειρισμός δεδομένων με SQL
Το σχεσιακό μοντέλο δεδομένων
Ένα σύστημα διαχείρισης βάσης δεδομένων (ΣΔΒΔ) (database management system (DBMS))
αποτελείται από ένα σύνολο δεδομένων και προγράμματα πρόσβασης στα δεδομένα αυτά.
Το σύνολο των δεδομένων καλείται βάση δεδομένων (database).
Στόχος του ΣΔΒΔ είναι η εύκολη και γρήγορη χρήση και ανάκτηση των δεδομένων.
Η διαχείριση των δεδομένων περιλαμβάνει:
- τον ορισμό δομών για τη αποθήκευση των δεδομένων
- τον ορισμό μεθόδων για τη διαχείριση των δεδομένων
Ο ορισμός της δομής της βάσης δεδομένων βασίζεται σε ένα μοντέλο δεδομένων
το οποίο ορίζει τον τρόπο που περιγράφονται τα δεδομένα, οι σχέσεις τους,
η σημασία τους και οι περιορισμοί πάνω στα δεδομένα αυτά.
Το σχεσιακό μοντέλο (relational model) δεδομένων
παριστάνει δεδομένα και τις σχέσεις τους ως ένα σύνολο πινάκων.
Κάθε πίνακας (table)
αποτελείται από στήλες (columns) με μοναδικά ονόματα.
Μια γραμμή (row) του πίνακα παριστάνει μια
σχέση (relationship) ανάμεσα σε ένα σύνολο από τιμές.
Ο πίνακας που ακολουθεί παριστάνει έναν τηλεφωνικό κατάλογο.
Αποτελείται από δύο στήλες και πέντε γραμμές.
Όνομα | Τηλέφωνο |
Γιώργος | 32560 |
Μαρία | 61359 |
Θανάσης | 98756 |
Λίνα | 78999 |
Πέτρος | 12356 |
Η SQL (structured query language) αποτελεί σήμερα την πιο διαδεδομένη γλώσσα
διαχείρισης σχεσιακών βάσεων δεδομένων.
Η SQL παρέχει δυνατότητες για:
- τον ορισμό, τη διαγραφή και τη μεταβολή πινάκων και κλειδιών,
- τη σύνταξη ερωτήσεων (queries),
- την εισαγωγή, διαγραφή και μεταβολή στοιχείων,
- τον ορισμό όψεων (views) πάνω στα δεδομένα,
- τον ορισμό δικαιωμάτων πρόσβασης,
- τον έλεγχο της ακεραιότητας των στοιχείων,
- τον έλεγχο συναλλαγών (transaction)
Η SQL είναι ορισμένη ως διεθνές πρότυπο.
Στις επόμενες ενότητες
θα εξετάσουμε ένα υποσύνολο της SQL όπως υποστηρίζεται από την
εγκατεστημένη στα εργαστήρια βάση δεδομένων Microsoft Access.
Στην περιγραφή της σύνταξης της SQL θα χρησιμοποιήσουμε τα παρακάτω σύμβολα:
- [ έκφραση ]
- η έκφραση εμφανίζεται προαιρετικά
- έκφραση1 | έκφραση2
- μπορεί να γραφεί η έκφραση1 ή η έκφραση2
- έκφραση ...
- η έκφραση μπορεί να επαναληφθεί
Τύποι δεδομένων
Τα δεδομένα κάθε στήλης ενός πίνακα πρέπει να έχουν ένα
συγκεκριμένο τύπο.
Οι βασικοί τύποι που υποστηρίζονται από την SQL είναι οι παρακάτω:
- ΒΙΤ
- Ναι ή Όχι
- CURRENCY
- Τιμή που παριστάνει με ακρίβεια αριθμούς από
-922.337.203.685.477,5808 έως 922.337.203.685.477,5807
- DATETIME
- Χρόνος
- SINGLE
- Αριθμός κινητής υποδιαστολή μονής ακρίβειας
- DOUBLE
- Αριθμός κινητής υποδιαστολή διπλής ακρίβειας
- SHORT
- Ακέραιος 2 byte (-32768 έως 32767)
- LONG
- Ακέραιος 4 byte (-2.147.483.648 έως 2.147.483.647)
- TEXT
- Κείμενο μέχρι 255 χαρακτήρες
- LONGTEXT
- Κείμενο μέχρι 1.2GB
Δημιουργία πινάκων
Νέοι πίνακες δημιουργούνται με την εντολή CREATE TABLE.
Αυτή συντάσσεται ως εξής:
CREATE TABLE όνομα_πίνακα (πεδίο1 τύπος [(μέγεθος)] [, πεδίο2 τύπος [(μέγεθος)] [, ...]]
Για παράδειγμα η εντολή
CREATE TABLE Customer (Name TEXT (20), AccountNum SHORT)
δημιουργεί τον πίνακα με όνομα Customer και με δύο στήλες:
την Name και την AccountNum.
Αλλαγές σε πίνακες
Η εντολή ALTER TABLE επιτρέπει την προσθήκη νέων στηλών ή τη διαγραφή
υπαρχόντων.
Η προσθήκη νέων στηλών γίνεται με τη σύνταξη:
ALTER TABLE όνομα_πίνακα ADD COLUMN πεδίο τύπος[(μέγεθος)]
Για παράδειγμα η εντολή:
ALTER TABLE Customer ADD COLUMN Notes TEXT(25)
προσθέτει μια νέα στήλη στον πίνακα Customer.
Η διαγραφή υπαρχόντων στηλών γίνεται με τη σύνταξη:
ALTER TABLE όνομα_πίνακα DROP COLUMN πεδίο
Για παράδειγμα η εντολή:
ALTER TABLE Customer DROP COLUMN Notes
αφαιρεί τη στήλη Notes από τον πίνακα Customer.
Τέλος, η εντολή DROP TABLE επιτρέπει τη διαγραφή πινάκων.
Για παράδειγμα η εντολή
DROP TABLE Customer
θα διαγράψει τον πίνακα Customer
Δείκτες
Η πρόσβαση στα στοιχεία ενός πίνακα γίνεται ταχύτερα όταν αυτά οργανωθούν
με τη βοήθεια δεικτών.
Ένας δείκτης ορίζεται για μια συγκεκριμένη στήλη ή στήλες και επιτρέπει τη
γρήγορη πρόσβαση σε γραμμές με βάση τιμές της συγκεκριμένης στήλης.
Ουσιαστικά όταν ορίζουμε έναν δείκτη το ΣΔΒΔ υλοποιεί μια δομή δεδομένων
(π.χ. ταξινομημένο ή κατακερματισμένο πίνακα ή δένδρο) για γρήγορη πρόσβαση
στα αντίστοιχα δεδομένα.
Δείκτες δημιουργούνται με την εντολή CREATE INDEX.
Η σύνταξή της είναι η παρακάτω:
CREATE [ UNIQUE ] INDEX όνομα_δείκτη
ON όνομα_πίνακα (πεδίο [ASC|DESC][, πεδίο [ASC|DESC], ...])
Η λέξη UNIQUE ορίζει πως δε θα επιτρέπονται διπλές εμφανίσεις μιας τιμής
για το συγκεκριμένο δείκτη.
Οι λέξεις ASC και DESC ορίζουν αύξουσα ή φθίνουσα ταξινόμηση του
πίνακα με βάση το δείκτη.
Παράδειγμα:
CREATE INDEX NameIdx ON Customer (Name)
Δημιουργεί το δείκτη NameIdx για τη στήλη Name στον πίνακα Customer.
Ένας δείκτης μπορεί να διαγραφεί με τη σύνταξη:
DROP INDEX όνομα_δείκτη ON όνομα_πίνακα
Προσθήκη στοιχείων
Προσθήκη δεδομένων σε έναν πίνακα γίνεται με την εντολή
INSERT INTO σύμφωνα με τη σύνταξη:
INSERT INTO όνομα_πίνακα [(πεδίο1[, πεδίο2[, ...]])]
VALUES (τιμή1[, τιμή2[, ...])
Παράδειγμα:
INSERT INTO Customer (Name, AccountNum) VALUES ("Papadopoulos", 1234)
Επιλογή
Επιλογή δεδομένων από έναν ή περισσότερους πίνακες γίνεται με την εντολή
SELECT σύμφωνα με την παρακάτω βασική σύνταξη:
SELECT πεδία
FROM πίνακες
[ WHERE κριτήρια ]
Απλό παράδειγμα:
SELECT Name, AccountNum FROM Customer WHERE Name LIKE "Pap*"
Τα πεδία μπορούν να είναι ονόματα πεδίων ή συγκεντρωτικές συναρτήσεις της
SQL πάνω σε πεδία.
Τέτοιες συναρτήσεις είναι οι παρακάτω:
- Avg
- Μέσος όρος
- Count
- Μέτρηση
- Min
- Ελάχιστο
- Max
- Μέγιστο
- Sum
- Σύνολο
Παράδειγμα:
SELECT Sum(AccountBalance) FROM Customer
Ο αστερίσκος ως ορισμός πεδίου επιλέγει όλα τα πεδία.
Τα κριτήρια αναζήτησης είναι εκφράσεις πάνω στα πεδία.
Ορισμένοι βασικοί τελεστές είναι οι:
- αριθμητικοί + - * / mod
- σύγκρισης < <= > >> = <> like
- λογικοί and or not
Παράδειγμα:
SELECT * FROM Customer WHERE Balance > 10000 AND Name LIKE "Papad*"
Βασικό στοιχείο του σχεσιακού μοντέλου είναι η αποθήκευση δεδομένων
σε πίνακες που σχετίζονται μεταξύ τους.
Έτσι για παράδειγμα ένας πίνακας μπορεί να φυλάει τα στοιχεία των πελατών
και ένας άλλος τους λογαριασμούς.
Με τον τρόπο αυτό τα στοιχεία ενός πελάτη που έχει πολλούς λογαριασμούς
σε μια τράπεζα φυλάσσονται μόνο μια φορά.
Ανάκτηση από δύο τέτοιους πίνακες γίνεται με τον προσδιορισμό τους
στο SELECT μαζί με τη χρήση της κατάλληλης έκφρασης που θα ενώσει τους
πίνακες.
Παράδειγμα:
SELECT Customer, Balance FROM Customers, Accounts WHERE
Customer.AccountId = Accounts.AccountId
Αλλαγή
Αλλαγή σε στοιχεία γίνεται με την εντολή UPDATE σύμφωνα με τη σύνταξη:
UPDATE όνομα_πίνακα
SET πεδίο = νέα_τιμή
WHERE κριτήρια;
Παράδειγμα:
UPDATE Accounts
SET Balance=100000
WHERE AccountId = 12233
Διαγραφή
Διαγραφή γραμμών από έναν πίνακα γίνεται με την εντολή DELETE σύμφωνα με
τη σύνταξη:
DELETE
FROM όνομα_πίνακα
WHERE κριτήρια
Παράδειγμα:
DELETE
FROM Accounts
WHERE
AccountId = 123232
SQL στη Microsoft Access
Για να δώσουμε εντολές SQL στη Microsoft Access ακολουθούμε την
παρακάτω διαδικασία:
- Δημιουργούμε μια κενή βάση δεδομένων
- View - Database objects - Queries
- New - Design view
- Show table - close
- View - SQL
- Γράφουμε την εντολή SQL που θέλουμε
- Query - Run
Τα αποτελέσματα της εντολής μπορούμε να τα δούμε στην περιοχή Tables.
Βιβλιογραφία
- D. D. Chamberlin,
M. M. Astrahan, K. P. Eswaran, P. P. Griffiths, R. A. Lorie, J. W. Mehl,
P. Reisner, and B. W. Wade.
SEQUEL 2: A unified approach to data definition manipulation and control.
IBM Journal of Research and Development, 20(6):560–575, November
1976.
- Peter Pin-Shan Chen.
The entity-relationship model — toward a unified view of data.
ACM Transactions on Database Systems, 1(1):9–36, March 1976.
- International Organization
for Standardization, Geneva, Switzerland.
Information technology — Database languages — SQL, 1992.
ISO/IEC 9075:1992.
- Henry F. Korth
and Abraham Silberschatz.
Database System Concepts.
McGraw-Hill, second edition, 1991.
Ασκήσεις
Άσκηση 8 (προαιρετική)
- Ορίστε με βάση SQL έναν τηλεφωνικό κατάλογο με τους φίλους σας.
- Εισάγετε μερικά ονόματα και τα τηλέφωνα
- Ανακτήστε ένα τηλέφωνο με τον τελεστή LIKE
- Μετρήστε τον αριθμό των φίλων σας με την έκφραση Count
Συναρτησιακός προγραμματισμός στη Lisp
Συναρτησιακός προγραμματισμός και η γλώσσα Lisp
Ο συναρτησιακός προγραμματισμός (functional programming)
βασίζεται στην αποτίμηση συναρτήσεων αντί για την εκτέλεση εντολών.
Προγράμματα βασισμένα στο συναρτησιακό προγραμματισμό είναι γραμμένα
με βάση συναρτήσεις που αποτιμούν βασικές τιμές.
Μια συναρτησιακή γλώσσα προγραμματισμού υποστηρίζει και ενθαρρύνει το
συναρτησιακό προγραμματισμό.
Βασικά χαρακτηριστικά του συναρτησιακού προγραμματισμού είναι:
Ορισμένες γνωστές συναρτησιακές γλώσσες προγραμματισμού είναι οι:
- Erlang
- Haskell
- Hope
- Lisp
- Miranda
- ML
- Scheme
Οι βασικές ιδέες της γλώσσας Lisp αναπτύχθηκαν από τον John McCarthy το 1956
σε ένα ερευνητικό πρόγραμμα για τεχνητή νοημοσύνη.
Στόχος του McCarthy ήταν η ανάπτυξη μιας αλγευρικής γλώσσας επεξεργασίας
λιστών για έρευνα στην τεχνητή νοημοσύνη.
Ανάμεσα στα έτη 1960 και 1965 υλοποιήθηκε η διάλεκτος Lisp 1.5 η οποία τη
δεκαετία του 1970 είχε οδηγήσει στις διαλέκτους MacLisp και InterLisp.
Στα μέσα της δεκαετίας του 1970 οι Sussman και Steele Jr. ανέπτυξαν
τη διάλεκτο Scheme με βάση ιδέες από τη σημασιολογία των γλωσσών
προγραμματισμού.
Στη δεκαετία του 1980 έγινε προσπάθεια για ενοποίηση των διαλέκτων της
Lisp κάτω από το πρότυπο της Common Lisp.
Απλές συναρτήσεις
Παράδειγμα (ορισμός της συνάρτησης του παραγοντικού):
(defun factorial (n)
(if (equal n 0)
1
(* n (factorial (- n 1)))))
Λίστες
- Στη Lisp μπορούμε να ορίσουμε μια λίστα (list)
από τιμές με τη σύνταξη '(τιμή1 τιμή2 τιμή3 ...)
- Η ειδική τιμή nil παριστάνει την κενή λίστα.
- Μπορούμε να αναφερθούμε σε σύμβολα με τη σύνταξη 'όνομα.
Παράδειγμα:
'(1 2 3 4 42)
'word
'('Maria 'John 'Aliki)
Βασικές συναρτήσεις επεξεργασίας λιστών είναι οι παρακάτω:
- (null list)
επιστρέφει αληθές αν η λίστα είναι κενή
- (cons val list)
επιστρέφει τη λίστα list με πρώτο το στοιχείο val,
- (car list)
επιστρέφει το πρώτο στοιχείο μιας λίστας,
- (cdr list)
επιστρέφει τα υπόλοιπα (όλα εκτός από το πρώτο)
στοιχεία μιας λίστας ή nil αν αυτά δεν υπάρχουν.
Με βάση τις συναρτήσεις αυτές μπορούμε να ορίσουμε πιο σύνθετες συναρτήσεις.
Length
Επιστρέφει το μήκος μιας λίστας.
(defun mylength (a)
(if (null a)
0
(+ (mylength (cdr a)) 1)))
Append
Ενώνει δύο λίστες.
(defun myappend (a b)
(if (null a)
b
(cons (car a) (myappend (cdr a) b))))
Reverse
Αντιστρέφει μια λίστα.
(defun myreverse (a)
(if (null a)
nil
(myappend (myreverse (cdr a)) (cons (car a) nil))))
Συναρτήσεις ανωτέρου βαθμού
- Μπορούμε να ορίσουμε μια
συνάρτηση ανωτέρου βαθμού (higher order function)
ορίζοντας ως ένα όρισμα της συνάρτησης μια άλλη συνάρτηση.
- Τη συνάρτηση-όρισμα μπορούμε να την εφαρμόσουμε πάνω σε κάποιες
τιμές με τη συνάρτηση apply η οποία λαμβάνει ως όρισμα μια συνάρτηση το
πρώτο όρισμα και μια λίστα με τα υπόλοιπα όρισματά της (ή nil) και επιστρέφει το
αποτέλεσμα της συνάρτησης εφαρμοσμένο στο όρισμα.
- Μπορούμε ακόμα να ορίσουμε δυναμικά μια τιμή τύπου συνάρτησης
με τη σύνταξη:
(lambda (όρισμα) τιμή)
Το παρακάτω παράδειγμα συνθέτουμε τις έννοιες αυτές για να ορίσουμε
τις συναρτήσεις map και reduce.
Map
Η συνάρτηση map μετατρέπει μια λίστα σε μια άλλη με βάση μια συνάρτηση που έχει
δοθεί ως όρισμα.
(defun mymap (f lst)
(if (null lst)
nil
(cons (apply f (cons (car lst) nil)) (mymap f (cdr lst)))))
Έτσι μπορούμε π.χ. να διπλασιάσουμε να στοιχεία της λίστας '(1 2 3) με την
κλήση:
(mymap (lambda (x) (* 2 x)) '(1 2 3))
(2 4 6)
Reduce
Η συνάρτηση reduce συμπυκνώνει μια λίστα εφαρμόζοντας αναδρομικά
τη συνάρτηση σε κάθε στοιχείο της λίστας αρχίζοντας από μια αρχική τιμή.
(defun myreduce (f v lst)
(if (null lst)
v
(apply f (cons (car lst) (cons (myreduce f v (cdr lst)) nil)))))
Έτσι μπορούμε να ορίσουμε συναρτήσεις όπως τις:
- sum επιστρέφει το σύνολο των τιμών μιας λίστας
(defun mysum (lst) (myreduce '+ 0 lst))
- product επιστρέφει το γινόμενο των τιμών μιας λίστας
(defun myproduct (lst) (myreduce '* 1 lst))
- alltrue επιστρέφει αληθές αν όλες οι τιμές μιας λίστας είναι αληθείς
(defun alltrue (lst) (myreduce 'and t lst))
- anytrue επιστρέφει αληθές αν τουλάχιστον μια τιμή μιας λίστας είναι αληθής
(defun anytrue (lst) (myreduce 'or nil lst))
Διερμηνευτής Lisp
-
Διερμηνευτή συμβατό με τα παραδείγματα των σημειώσεων
μπορείτε να βρείτε εδώ.
-
Τεκμηρίωση για το διερμηνευτή
μπορείτε να βρείτε εδώ.
- Η πιο εύκολη διαδικασία για τη χρήση του διερμηνευτή είναι:
- File - Open / Load το αρχείο με τις ορισμένες συναρτήσεις
- Δοκιμή της συνάρτησης μέσα στο παράθυρο.
-
Ο πηγαίος κώδικας του διερμηνευτή (νέα έκδοση)
και υλοποιήσεις για άλλες πλατφόρμες υπάρχουν
διεύθυνση εδώ (http://almy.us/xlisp.html).
Βιβλιογραφία
- Ellis Horowitz
Βασικές αρχές γλωσσών προγραμματισμού.
Δεύτερη αμερικάνικη έκδοση.
σ. 367-398 Εκδόσεις Κλειδάριθμος, 1984.
- Harold Abelson,
Gerald Jay Sussman, and Jullie Sussman.
Structure and Interpretation of Computer Programs.
MIT Press, 1990.
- Anthony J. Field and
Peter G. Harrison.
Functional Programming.
Addison-Wesley, 1988.
- John Hughes.
Why functional programming matters.
In David A. Turner, editor, Research Topics in Functional
Programming, chapter 2, pages 17–42. Addison-Wesley, 1990.
Also appeared in the April 1989 issue of The Computer Journal.
- Peter H. Salus, editor.
Handbook of Programming Languages, volume III: Functional and Logic
Programming Languages.
Macmillan Technical Publishing, 1998.
Ασκήσεις
Άσκηση 9
-
Οι δύο πρώτοι αριθμοί της ακολουθίας Fibonacci είναι 0 και 1.
Κάθε επόμενος αριθμός είναι το άθροισμα των δύο προηγούμενων.
Γράψτε τη συνάρτηση που επιστρέφει τον νοστό αριθμό Fibonacci.
(Οι αριθμοί Fibonacci απαντώνται συχνά στη φύση π.χ. στον αριθμό πετάλων
των λουλουδιών και στις σπείρες από τα κουκουνάρια και τις αχιβάδες).
-
Γράψτε τη συνάρτηση (member list val) που επιστρέφει αληθές αν η τιμή val
περιέχεται στη λίστα list.
Κατηγορήματα στην Prolog
Εισαγωγή
Η προστακτικές γλώσσες προγραμματισμού όπως
η Fortran, η Pascal και η C έχουν κατασκευαστεί και να διευκολύνουν
τον προγραμματισμό υπολογιστών αρχιτεκτονικής
von Neumann (επεξεργαστής που εκτελεί κώδικα και επεξεργάζεται δεδομένα
από τη μνήμη).
Ιστορικά, ο στόχος για το σχεδιασμό των γλωσσών αυτών είναι να γίνει ο
προγραμματισμός του υπολογιστή προσιτός στον άνθρωπο.
Έτσι, η διαδικασία της λύσης ενός προβλήματος με υπολογιστή
εμφανίζεται διχοτομημένη ανάμεσα στην
ανάλυση του προβλήματος και, στη συνέχεια, την κωδικοποίησή του σε υπολογιστή.
Η εξέλιξη της ισχύος των υπολογιστών και του αντίστοιχου θεωρητικού
υπόβαθρου μας έχει επιτρέψει να εξετάζουμε διαφορετικές προσεγγίσεις
επίλυσης προβλημάτων με υπολογιστή που να βασίζονται όχι στο να κάνουν
την αρχιτεκτονική του υπολογιστή προσιτή στον άνθρωπο, αλλά στο να κάνουν
τη διατύπωση των προβλημάτων από τον άνθρωπο προσιτή στον υπολογιστή.
Έτσι, τόσο ο συναρτησιακός όσο και ο λογικός προγραμματισμός επιτρέπουν
τη διατύπωση του προβλήματος που πρέπει να επιλυθεί με βάση έναν συμβολισμό
και αφήνουν στον υπολογιστή να λύσει το πρόβλημα με βάση τη διατύπωσή του.
Η βάση του λογικού προγραμματισμού μπορεί να διατυπωθεί ως εξής:
- πρόγραμμα = σύνολο από αξιώματα
- υπολογισμός = απόδειξη του στόχου με βάση το πρόγραμμα
ή τη σύνοψή της από τον Bob Kowalski:
- αλγόριθμος = λογική + έλεγχος
Η ιστορία του λογικού προγραμματισμού και της Prolog μπορεί να συνοψιστεί
από τους παρακάτω σταθμούς:
- J. Robinson (1965)
- Διατύπωση του αλγόριθμου της
ταυτοποίησης (unification).
Ο αλγόριθμος αυτός βρίσκει ένα σύνολο από στοιχειώδεις αντικαταστάσεις
σε μεταβλητές για να καταστήσει δύο όρους ταυτόσημους.
- R. Kowalski (1974)
- Διαδικασιακή ερμηνεία προτάσεων Horn.
Προτάσεις της μορφής Α αν Β1 και Β2 και ... Βν
μπορούν να ερμηνευτούν διαδικασιακά ως: για να ισχύει το Α,
πρέπει να ισχύει το Β1 και το Β2 και ... το Βν.
- A. Colmerauer (1973)
- Υλοποίηση διερμηνευτή της Prolog σε Fortran.
- D. Warren (1977)
- Υλοποίηση μεταγλωττιστή της Prolog (σε ενδιάμεση
γλώσσα την Warren Abstract Machine).
- Ιαπωνία (1981)
- Πρόγραμμα υπολογιστών πέμπτης γενιάς.
Έδωσε ώθηση στην Prolog υιοθετώντας το λογικό προγραμματισμό
ως βασική τεχνολογία του προγράμματος.
Σχέσεις
Η βάση της Prolog είναι τα
γεγονότα (facts), οι
κανόνες (rules) και οι
ερωτήσεις (queries).
Υπάρχει μόνο μια δομή δεδομένων, ο όρος (term).
Τα γεγονότα ορίζουν μια αληθή σχέση ανάμεσα σε αντικείμενα.
Παράδειγμα 1 (ορισμός σχέσεων ανάμεσα σε ακέραιούς):
sum(0, 0, 0).
sum(0, 1, 1).
sum(1, 0, 1).
sum(1, 1, 2).
Παράδειγμα 2 (ορισμός σχέσεων ανάμεσα σε ανθρώπους):
father(anthi, giannis).
father(petros, giannis).
father(thanasis, giannis).
father(iro, giannis).
father(nikos, thanasis).
father(giorgos, thanasis).
mother(anthi, maria).
Οι λέξεις nikos, petros, anthi, στο παραπάνω παράδειγμα είναι σταθερές
της γλώσσας (όπως και οι ακέραιοι στο προηγούμενο).
Ονομάζονται άτομα (atoms) και γράφονται με αρχικό
μικρό γράμμα.
Με βάση τα παραπάνω γεγονότα μπορούμε να εκτελέσουμε ερωτήσεις για να μάθουμε
αν μια σχέση είναι αληθής ή ψευδής.
Όταν η Arity Prolog βρει μια αληθή λύση στην ερώτησή μας μας εμφανίζει το
σύμβολο ->.
Στο σημείο εκείνο μπορούμε να πατήσουμε Enter για να δηλώσουμε πως μας
ικανοποιεί η λύση, ή ; για να δηλώσουμε πως θέλουμε να δούμε και άλλες
λύσεις.
Παράδειγμα:
?- sum(0, 0, 0).
->
yes
?- sum(1, 1, 2).
->
yes
?- sum(1, 1, 0).
no
?- sum(5, 3, 9).
no
Λογικές μεταβλητές
Ερωτήσεις με μεταβλητές
Οι λογικές μεταβλητές στην Prolog (γράφονται με αρχικό κεφαλαίο γράμμα)
μπορούν να χρησιμοποιηθούν για να τεθούν ερωτήσεις για να μάθουμε κάποιο
στοιχείο από τα γεγονότα που έχουμε ορίσει.
Παράδειγμα:
?- sum(1, 1, X).
X = 2
yes
?- sum(X, 1, X).
no
?- sum(X, X, 2).
X = 1
yes
?- father(iro, X).
X = giannis
yes
?- father(X, Y).
X = anthi
Y = giannis ->;
X = petros
Y = giannis ->;
X = thanasis
Y = giannis ->;
X = iro
Y = giannis ->;
X = nikos
Y = thanasis ->
yes
Γεγονότα με μεταβλητές
Με βάση τις μεταβλητές μπορούμε ακόμα να ορίσουμε και
γενικευμένα γεγονότα (universal facts).
Για παράδειγμα για να ορίσουμε πως όλοι αγαπούν τη Μαίρη
γράφουμε:
loves(X, mairi).
Όροι
Με βάση τα παραπάνω μπορούμε να ορίσουμε τη δομή δεδομένων της
Prolog, τον όρο.
Οι όροι ορίζονται αναδρομικά.
Ένας όρος μπορεί να είναι:
- σταθερά, δηλαδή άτομο ή αριθμός
- μεταβλητή
- άτομο(όρος1, ... όρος_ν)
Παραδείγματα όρων:
sum(0, 0, 1)
car(color(red), engine(1600), abs, speed(172), doors(2,2,1))
loves(X, Y)
Σύζευξη
Μπορούμε να ενώσουμε ερωτήσεις μεταξύ τους με το κόμμα (,) για να
απαιτήσουμε να είναι και οι δύο αληθείς:
?- sum(0, 1, 1), sum(1, 1, 2).
yes
?- sum(0, 1, 1), sum(1, 1, 3).
no
Μπορούμε να ορίσουμε ακόμα κανόνες με βάση ένα γεγονός και κάποιες
σχετικές ερωτήσεις με την παρακάτω σύνταξη:
a :-
b1,
b2,
bn.
Ο κανόνας διαβάζεται ως εξής:
ισχύει το a αν ισχύει το b1 και το b2 και το bn.
Αν ορίσουμε παραπάνω από έναν κανόνα για το ίδιο γεγονός τότε
οι κανόνες ισχύουν διαζευκτικά (αρκεί να ισχύει ένας).
Παράδειγμα:
parent(X, Y) :-
father(X, Y).
parent(X, Y) :-
mother(X, Y).
?- parent(anthi, X).
X = giannis ->;
X = maria
yes
?- parent(X, giannis).
X = anthi ->;
X = petros ->;
X = thanasis ->;
X = iro ->;
no
Παράδειγμα:
grandfather(X, Y) :-
parent(X, Z),
father(Z, Y).
?- grandfather(X, Y).
X = giannis
Y = nikos ->;
X = giannis
Y = giorgos ->;
no
Αριθμητική με αναδρομή
Με βάση τον αναδρομικό ορισμό των φυσικών αριθμών μπορούμε να τους ορίσουμε
αξιωματικά και στην Prolog.
Ορίζουμε τον όρο s(X) να παριστάνει τον επόμενο φυσικό αριθμό από το X.
Έτσι, ορίζοντας το 0 το 1 είναι s(0), το 2 είναι s(s(0)), κ.λπ.
Ο κανόνας nat ορίζει τους φυσικούς αριθμούς.
Μπορούμε με επαγωγή να αποδείξουμε πως
όλοι οι φυσικοί παράγονται και αναγνωρίζονται από τον nat.
nat(0).
nat(s(X)) :-
nat(X).
?- nat(0).
yes
?- nat(s(0)).
yes
?- nat(s(s(s(s(0))))).
yes
?- nat(X).
X = 0 ->;
X = s(0) ->;
X = s(s(0)) ->;
X = s(s(s(0))) ->;
X = s(s(s(s(0)))) ->
X = s(s(s(s(s(0))))) ->;
X = s(s(s(s(s(s(0)))))) ->;
X = s(s(s(s(s(s(s(0))))))) ->;
X = s(s(s(s(s(s(s(s(0)))))))) ->;
X = s(s(s(s(s(s(s(s(s(0))))))))) ->;
X = s(s(s(s(s(s(s(s(s(s(0)))))))))) ->
...
Με βάση τον ορισμό αυτό μπορούμε να ορίσουμε μια σχέση διάταξης:
less(0, X) :-
nat(X).
less(s(X), s(Y)) :-
less(X, Y).
?- less(s(0), 0).
no
?- less(s(0), s(s(0))).
yes
?- less(X, s(s(s(s(0))))).
X = 0 ->;
X = s(0) ->;
X = s(s(0)) ->;
X = s(s(s(0))) ->;
X = s(s(s(s(0)))) ->;
no
Με παρόμοιο τρόπο μπορούμε να ορίσουμε και την πρόσθεση:
plus(0, X, X) :-
nat(X).
plus(s(X), Y, s(Z)) :-
plus(X, Y, Z).
Είναι αξιοσημείωτο πως το κατηγόρημα της πρόσθεσης με τη μορφή που
έχει οριστεί μπορεί να υπολογίσει άθροισμα, διαφορά, όρους που
δημιουργούν ένα άθροισμα, ή να ελέγξει αν ένα άθροισμα είναι αληθές:
?- plus(s(0), s(0), X).
X = s(s(0))
yes
?- plus(s(0), X, s(s(s(0)))).
X = s(s(0))
yes
?- plus(X, Y, s(s(0))).
X = 0
Y = s(s(0)) ->;
X = s(0)
Y = s(0) ->;
X = s(s(0))
Y = 0 ->;
no
?- plus(s(0), 0, 0).
no
Με τη χρήση του αθροίσματος μπορούμε να ορίσουμε και το γινόμενο:
times(0, X, 0)
times(s(X), Y, Z) :-
times(X, Y, W),
plus(W, Y, Z).
Το κατηγόρημα για το γινόμενο που έχει οριστεί με τον τρόπο αυτό έχει
και αυτό παρόμοια ευελιξία:
?- times(s(s(0)), s(s(s(0))), X).
X = s(s(s(s(s(s(0))))))
yes
?- times(X, s(s(0)), s(s(s(s(0))))).
X = s(s(0)) ->.
yes
?- times(X, Y, s(s(s(s(0))))).
X = s(0)
Y = s(s(s(s(0)))) ->;
X = s(s(0))
Y = s(s(0)) ->;
?- times(s(0), s(0), s(0)).
yes
Υπολογισμοί στην Prolog
Αν και ο παραπάνω ορισμός των ακεραίων είναι αξιοθαύμαστος για τη λιτότητα και
την πληρότητά του, οι υλοποιήσεις της Prolog για υπολογισμούς αριθμών
χρησιμοποιούν το κατηγόρημα is(X, Y) και την υλοποίηση των αριθμών από
τον επεξεργαστή του υπολογιστή.
Αυτό υπολογίζει το αποτέλεσμα της αριθμητικής έκφρασης στο Y και την
ταυτοποιεί με το X.
Το κατηγόρημα αυτό είναι μεν αποδοτικό, δε συμπεριφέρεται όμως με την
ευελιξία που δείξαμε στα παραπάνω παραδείγματα.
Παράδειγμα:
?- is(X, 1 + 2).
X = 3
?- is(2, 1 + 1).
yes
?- is(Y, 3 * 8 + 4 / 5).
Y = 24.8
yes
?- is(2, X + X).
no
Λίστες
Οι λίστες στην Prolog ορίζονται με τη σύνταξη [Head | Tail] όπου
Head είναι το στοιχείο που αποτελεί την κεφαλή της λίστας και
Tail τα υπόλοιπα στοιχεία.
Η σύνταξη αυτή αποτελεί απλώς συντομογραφία για όρους των οποίων το
όνομα είναι η τελεία (.).
Με βάση τον ορισμό των λιστών μπορούμε πολύ εύκολα να ορίσουμε σύνθετους
αλγόριθμους όπως την ταξινόμηση quick sort:
quicksort([X|Xs], Ys) :-
partition(Xs, X, L, B),
quicksort(L, Ls),
quicksort(B, Bs),
append(Ls, [X | Bs], Ys).
quicksort([], []).
partition([X|Xs], Y, [X|Ls], Bs) :-
X =< Y,
partition(Xs, Y, Ls, Bs).
partition([X|Xs], Y, Ls, [X|Bs]) :-
X > Y,
partition(Xs, Y, Ls, Bs).
partition([], Y, [], []).
append([], X, X).
append([H | T], X, [H | Z]) :-
append(T, X, Z).
?- quicksort([4,2,8,12,1,7], X).
X = [1,2,4,7,8,12] ->.
yes
Συμβολική επεξεργασία
Με βάση μεταβλητές και όρους μπορούμε να γράψουμε ένα πρόγραμμα
που να αναγνωρίζει πολυώνυμα ορισμένα με τον παρακάτω τρόπο:
- sum(A, B) (άθροισμα)
- diff(A, B) (διαφορά)
- prod(A, B) (γινόμενο)
- quot(A, B) (πηλίκο)
- pow(A, B) (ύψωση σε δύναμη)
- τη μεταβλητή X
- καθώς και ακεραίους.
polynomial(X, X).
polynomial(N, X) :-
number(N).
polynomial(sum(A, B), X) :-
polynomial(A, X),
polynomial(B, X).
polynomial(diff(A, B), X) :-
polynomial(A, X),
polynomial(B, X).
polynomial(prod(A, B), X) :-
polynomial(A, X),
polynomial(B, X).
polynomial(quot(A, B), X) :-
polynomial(A, X),
number(B).
polynomial(pow(A, B), X) :-
polynomial(A, X),
integer(B).
Ερωτήσεις:
?- p(sum(x, 1), x).
->.
yes
?- p(sum(x, 1), y).
no
?- polynomial(sum(prod(5,pow(x, 2)), prod(2, x)), x).
->.
yes
?- polynomial(sum(prod(5,pow(x, 2)), pow(y, 3)), x).
no
?- polynomial(sum(prod(5,pow(x, 2.1)), prod(2, x)), x).
no
Σύντομες οδηγίες για τη χρήση της Arity Prolog
- Αντιγράφετε τα αρχεία c:\apps\arity\bin\dos\ari σε έναν
δικό σας κατάλογο (κάτω από το home σας).
- Τρέχετε την εντολή api.
- Στο ?- που εμφανίζεται μπορείτε να πληκτρολογήσετε ερωτήσεις.
- Για να πληκτρολογήσετε κανόνες πατάτε Switch (ALT-S) και γράφετε
τους κανόνες που θέλετε στο διορθωτή που εμφανίζεται.
- Πριν βγείτε από το διορθωτή βεβαιωθείτε πως η εντολή
Buffers - Reconsult on exit είναι ενεργοποιημένη (ALT-B ALT-C).
- Για να βγείτε από το διορθωτή επιλέγετε πάλι Switch (ALT-S).
- Το αρχείο κειμένου arity.hlp περιέχει στοιχεία για
όλα τα ορισμένα από την Arity Prolog κατηγορήματα.
Βιβλιογραφία
- Μ. Κατζουράκη, Μ. Γεργατσούλης και Σ. Κόκκοτος
PROγραμματίζοντας στη LOGική.
Εκδόσεις Νέων Τεχνολογιών, 1991.
- Christopher John Hogger.
Introduction to Logic Programming.
Academic Press, 1984.
- Robert Kowalski.
Predicate logic as programming language.
In Jack L. Rosenfeld, editor, Information Processing 74, Proceedings of
IFIP congress 74, pages 569–574, Stockholm, Sweden, August 1974.
International Federation of Information Processing, North-Holland.
- Robert Kowalski.
Logic as a computer language.
In Keith L. Clark and Sten-Åke Tärnlund, editors, Logic
Programming, pages 3–16. Academic Press, 1982.
- Francis G. McCabe.
Logic and Objects.
Prentice Hall, 1992.
- Richard A. O'Keefe.
The
Craft of Prolog.
MIT Press, 1990.
- Uday S. Reddy.
On the relationship between logic and functional languages.
In Doug DeGroot and Gary Lindstrom, editors, Logic Programming,
Functions, Relations and Equations, pages 3–36. Prentice Hall,
Englewood Cliffs, NJ, USA, 1986.
- J. A. Robinson.
A machine-oriented logic based on the resolution principle.
Journal of the ACM, 12(1):23–41, January 1965.
- Peter H. Salus, editor.
Handbook of Programming Languages, volume III: Functional and Logic
Programming Languages.
Macmillan Technical Publishing, 1998.
- Leon Sterling and
Ehud Shapiro.
The
Art of Prolog.
MIT Press, 1986.
- David H. D. Warren.
An abstract Prolog instruction set.
Technical Note 309, SRI International, Artificial Intelligence Center,
Computer Science and Technology Division, 333 Ravenswood Ave., Menlo Park,
CA, USA, October 1983.
Ασκήσεις
Άσκηση 10
-
Ορίστε σε Prolog το κατηγόρημα
polynomial_value(P, X, V).
όπου:
- Pείναι ένα πολυώνυμο ορισμένο με βάση τους όρους:
- sum(A, B) (άθροισμα)
- diff(A, B) (διαφορά)
- prod(A, B) (γινόμενο)
- pow(A, B) (ύψωση σε δύναμη)
- τη μεταβλητή x (προσοχή, μικρό γράμμα)
- καθώς και ακεραίους.
- X η τιμή της μεταβλητής x
- V η τιμή του πολυωνύμου.
Παράδειγμα:
?- polynomial_value(prod(5, pow(x, 3)), 3, X).
X = 135 ->
yes
?- polynomial_value(sum(prod(5, pow(x, 3)), prod(x, 5)), 4, X).
X = 340 ->.
yes
Την ύψωση ενός αριθμού σε δύναμη μπορείτε να την ορίσετε ως εξής:
raise(X, 0, 1).
raise(X, N, Y) :-
N2 is N - 1,
raise(X, N2, K),
Y is K * X.
Ταυτοχρονισμός στα Windows NT
Παραλληλία σε επίπεδο υλικού
Παραλληλία με τη χρήση αγωγών
- Διεργασίες που διαβάζουν στοιχεία και παράγουν αποτελέσματα
- Επικοινωνία μέσα από έναν αγωγό (pipe)
- Αναμονή για γράψιμο ή ανάγνωση
Παραδείγματα
Ανάγνωση αποτελεσμάτων
υπολογισμός |
μετατροπή σε λέξη |
φωνητική ανάγνωση
bc |
number |
speak
Συχνότητα λέξεων
Μετατροπή κειμένου σε λέξεις |
φιλτράρισμα λέξεων |
ταξινόμηση |
μέτρηση κοινών γραμμών |
ταξινόμηση
tr -cs A-Za-z '\012' |
grep '^A-Za-z' |
sort |
uniq -c |
sort -rn
Συνθήκες ανταγωνισμού
Συνθήκες ανταγωνισμού ονομάζονται οι
συνθήκες κατά τις οποίες το τελικό αποτέλεσμα της εκτέλεσης
μιας σειράς διεργασιών κάτω από αυτές εξαρτάται από τη σειρά εκτέλεσής
τους.
Παράδειγμα
int counter;
process_one()
{
int i;
i = counter;
i = i + 1;
counter = i;
}
process_two()
{
int i;
i = counter;
i = i + 1;
counter = i;
}
Κρίσιμα τμήματα
Το τμήμα του κώδικα μιας διεργασίας το οποίο δεν είναι ασφαλές
να εκτελεστεί παράλληλα με άλλη διεργασία ονομάζεται
κρίσιμο τμήμα (critical section).
Κάθε λύση στο πρόβλημα του
αμοιβαίου αποκλεισμού (mutual exclusion)
μεταξύ των κρίσιμων τμημάτων πρέπει να εξασφαλίζει:
- Μια το πολύ διεργασία μπορεί να εκτελεί ένα κρίσιμο τμήμα.
- Δεν επιτρέπονται υποθέσεις σχετικά με την ταχύτητα ή το πλήθος των
επεξεργαστών.
- Διεργασία που δε βρίσκεται σε κρίσιμο τμήμα δεν επιτρέπεται να αναστείλει
άλλες διεργασίες
- Ο χρόνος στον οποίο μια διεργασία θα εισέλθει στο κρίσιμο τμήμα της
πρέπει να είναι πεπερασμένος.
Ενεργός αναμονή
Ορισμένοι τρόποι για την επίτευξη του αμοιβαίου αποκλεισμού είναι οι
παρακάτω:
- Απενεργοποίηση των διακοπών
- Αυστηρή εναλλαγή
- Λύση του Peterson (προσθήκη μεταβλητής "ενδιαφέροντος")
- Η εντολή TSL
Όλες οι παραπάνω λύσεις επιβάλουν στη διαδικασία που αναμένει
να εκτελεί εντολές.
Απενεργοποίηση και αφύπνιση
Η οργάνωση της διαδιεργασιακής επικοινωνίας
γίνεται χρησιμοποιώντας αφηρημένες
βασικές αρχές διαδιεργασιακής επικοινωνίας (interprocess communication primitives).
To ζεύγος
είναι μια τέτοια αρχή.
Με τις κλήσεις αυτές μπορεί να εκφραστεί το πρόβλημα του
παραγωγού-καταναλωτή (producer-consumer).
const N = 100;
int queue[N];
int count;
void
producer(void)
{
int item;
for (;;) {
create(&item);
if (count == N)
sleep();
count = count + 1;
queue[count - 1] = item;
if (count == 1)
wakeup(consumer);
}
}
void
consumer(void)
{
int item;
for (;;) {
if (count == 0)
sleep();
item = queue[count - 1];
count = count - 1;
if (count == N - 1)
wakeup(producer);
consume(&item);
}
}
Η παραπάνω λύση έχει πρόβλημα συνθήκης ανταγωνισμού ως προς
τη χρήση της μεταβλητής count.
Σημαφόροι
To ζεύγος των
σημαφόρων (semaphores)
επιτρέπει την οργάνωση της διαδιεργασιακής επικοινωνίας
μια και αυτοί εκφράζουν
ατομικές ενέργειες (atomic action).
Με τις κλήσεις αυτές μπορεί να λυθεί το πρόβλημα του
παραγωγού-καταναλωτή.
const N = 100;
semaphore mutex = 1;
semaphore empty = N;
semaphore full = 0;
void
producer(void)
{
int item;
for (;;) {
create(&item);
signal(&empty);
signal(&mutex);
count = count + 1;
queue[count - 1] = item;
wait(&mutex)
wait(&full);
}
}
void
consumer(void)
{
int item;
for (;;) {
signal(&full);
signal(&mutex);
item = queue[count - 1];
count = count - 1;
wait(&mutex);
wait(&empty);
consume(&item);
}
}
Παρακολουθητές
Οι
παρακολουθητές (monitors) (ή ελεγκτές)
είναι δομή της γλώσσας προγραμματισμού που εξασφαλίζει ότι
σε μια συγκεκριμένη χρονική στιγμή
μπορεί να εκτελείται.
το πολύ μια διαδικασία μέσα στον παρακολουθητή.
Η δομή αυτή εξασφαλίζει τον αμοιβαίο αποκλεισμό κρισίμων τμημάτων.
H αναστολή διεργασιών που δεν μπορούν να συνεχίσουν εξασφαλίζεται
με τη χρήση των
μεταβλητών συνθήκης (condition variables)
Μεταβίβαση μηνύματος
Άλλος τρόπος διαδιεργασιακής επικοινωνίας είναι η χρήση της
μεταβίβαση μηνύματος (message passing) που
μπορεί να εκφραστεί με τις διαδικασίες:
- send(destination, message)
- receive(destination, message)
Ο συντονισμός της αποστολής και της παραλαβής μπορεί να γίνει:
Με τις κλήσεις αυτές μπορεί να εκφραστεί το πρόβλημα του
παραγωγού-καταναλωτή.
const N = 100;
int queue[N];
void
producer(void)
{
int item;
message m;
for (;;) {
create(&item);
receive(consumer, &m);
build_message(item, &m);
send(consumer, &m);
}
}
void
consumer(void)
{
int item;
message m;
for (i = 0; i < 100; i++)
send(producer, &m);
for (;;) {
receive(producer, &m);
extract_item(&m, &item);
send(producer, &m);
consume(&item);
}
}
Κλασικά προβλήματα
To πρόβλημα των συνδαιτημόνων φιλοσόφων
Λύσεις (σωστές και λανθασμένες):
- Λαμβάνουμε το αριστερό πιρούνι και περιμένουμε το δεξί
- Χρήση ενός σημαφόρου για όλους τους φιλοσόφους
- Χρήση ενός σημαφόρου ανά φιλόσοφο και μεταβλητής κατάστασης
Ταυτοχρονισμός στα Windows NT
Νηματικές διεργασίες
Οι
νηματικές διεργασίες (threads)
είναι ένας τρόπος ταυτοχρονισμού στον οποίο
οι διεργασίες μοιράζονται τα όλα τα δεδομένα εκτός από αυτά που
υπάρχουν στη στοίβα (δηλ. τις τοπικές μεταβλητές).
Η δημιουργία τους υποστηρίζεται συνήθως από το λειτουργικό σύστημα και
το μεταγλωττιστή.
Στα Windows NT μπορούμε να δημιουργήσουμε πολλαπλές νηματικές διεργασίες
με τη συνάρτηση _beginthread
Χρησιμοποιείται ως εξής:
void
thread_function(int *parameter)
{
}
/* Used to pass a parameter to the thread */
int parameter_value;
main()
{
parameter = 42;
_beginthread(thread_function, 0, ¶meter);
}
Ένα νήμα μπορεί να τερματίσει τη λειτουργία του με τη συνάρτηση
_endthread.
Σημαφόροι
Στα Windows 32 (95/98/NT/2000/...) μια σειρά από συναρτήσεις επιτρέπουν
το χειρισμό σημαφόρων.
-
Η συνάρτηση CreateMutex δημιουργεί και επιστρέφει έναν
νέο σημαφόρο.
Χρησιμοποιείται ως εξής:
HANDLE mutex; /* Declare semaphore variable */
mutex = CreateMutex(NULL, FALSE, NULL); /* Create semaphore */
-
Η συνάρτηση WaitForSingleObject είναι η αντίστοιχη της WAIT.
Χρησιμοποιείται ως εξής:
WaitForSingleObject(mutex, INFINITE);
-
Η συνάρτηση ReleaseMutex είναι η αντίστοιχη της SIGNAL.
Χρησιμοποιείται ως εξής:
ReleaseMutex(mutex);
Παράδειγμα αποκλεισμού περιοχής:
HANDLE mutex= CreateMutex(NULL, FALSE, NULL);
WaitForSingleObject(mutex, INFINITE);
/* Critical work starts here */
fseek(fp, desired_position, 0L );
fwrite(data, sizeof( data ), 1, fp );
/* Critical work ends here */
ReleaseMutex(mutex);
Μεταγλώττιση
Δηλώσεις για τις συναρτήσεις που αναφέραμε στις προηγούμενες παραγράφους
υπάρχουν στην επικεφαλίδα <windows.h> και <process.h>.
Για να μεταγλωττίσουμε ένα πρόγραμμα που περιέχει νηματικές διεργασίες
προσθέτουμε στην εντολή του μεταγλωττιστή τις παραμέτρους
/D_MT /MT .
Παράδειγμα:
cl -D_MT /MT test.c
Παράδειγμα εφαρμογής
Το παρακάτω πρόγραμμα, bounce.c (βασισμένο σε πρόγραμμα που
περιέχεται ως παράδειγμα στην τεκμηρίωση της Microsoft Visual C++)
δημιουργεί ένα νέο νήμα που
κινεί ένα πρόσωπο στην οθόνη κάθε φορά που πληκτρολογούμε A ή a.
Τερματίζει τη λειτουργία του όταν πληκτρολογήσουμε q ή Q.
/*
* Bounce - Creates a new thread each time the letter 'a' is typed. Each
* thread bounces a happy face of a different color around the screen. All
* threads are terminated when the letter 'Q' is entered.
*
* This program requires the multithread library. For example, compile with the
* following command line: CL /MT BOUNCE.C
*/
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <conio.h>
#include <process.h>
const int MAX_THREADS=32;
/*
* getrandom returns a random number between min and max, which must be in
* integer range.
*/
static int
getrandom(int min, int max)
{
return ((rand() % (int)(((max) + 1) - (min))) + (min));
}
void main(void); /* Thread 1: main */
void KbdFunc(void); /* Keyboard input, thread dispatch */
void BounceProc(char *MyID); /* Threads 2 to n: display */
void ClearScreen(void); /* Screen clear */
void ShutDown(void); /* Program shutdown */
void WriteTitle(int ThreadNum); /* Display title bar information */
HANDLE hConsoleOut; /* Handle to the console */
HANDLE hRunMutex; /* "Keep Running" mutex */
HANDLE hScreenMutex; /* "Screen update" mutex */
int ThreadNr; /* Number of threads started */
CONSOLE_SCREEN_BUFFER_INFO csbiInfo; /* Console information */
void
main()
{ /* Thread One */
/* Get display screen information & clear the screen. */
hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfo(hConsoleOut, &csbiInfo);
ClearScreen();
WriteTitle(0);
/* Create the mutexes and reset thread count. */
hScreenMutex = CreateMutex(NULL, FALSE, NULL); /* Cleared */
hRunMutex = CreateMutex(NULL, TRUE, NULL); /* Set */
ThreadNr = 0;
/* Start waiting for keyboard input to dispatch threads or exit. */
KbdFunc();
/* All threads done. Clean up handles. */
CloseHandle(hScreenMutex);
CloseHandle(hRunMutex);
CloseHandle(hConsoleOut);
}
/*
* Finish processing
*/
void
ShutDown(void)
{ /* Shut down threads */
while (ThreadNr > 0) {
/* Tell thread to die and record its death. */
ReleaseMutex(hRunMutex);
ThreadNr--;
}
/* Clean up display when done */
WaitForSingleObject(hScreenMutex, INFINITE);
ClearScreen();
}
/*
* Read an process keyboard commands
*/
void
KbdFunc(void)
{ /* Dispatch and count threads. */
int KeyInfo;
do {
KeyInfo = _getch();
if (tolower(KeyInfo) == 'a' && ThreadNr < MAX_THREADS) {
ThreadNr++;
_beginthread(BounceProc, 0, &ThreadNr);
WriteTitle(ThreadNr);
}
} while (tolower(KeyInfo) != 'q');
ShutDown();
}
/*
* Bounce the face around the screen.
* This procedure is run by each thread.
*/
void
BounceProc(char *MyID)
{
char MyCell, OldCell;
WORD MyAttrib, OldAttrib;
char BlankCell = 0x20;
COORD Coords, Delta;
COORD Old = {0, 0};
DWORD Dummy;
/* Generate update increments and initial display coordinates. */
srand((unsigned) *MyID * 3);
Coords.X = getrandom(0, csbiInfo.dwSize.X - 1);
Coords.Y = getrandom(0, csbiInfo.dwSize.Y - 1);
Delta.X = getrandom(-3, 3);
Delta.Y = getrandom(-3, 3);
/* Set up "happy face" & generate color attribute from thread number. */
if (*MyID > 16)
MyCell = 0x01; /* outline face */
else
MyCell = 0x02; /* solid face */
MyAttrib = *MyID & 0x0F;/* force black background */
do {
/* Wait for display to be available, then lock it. */
WaitForSingleObject(hScreenMutex, INFINITE);
/* If we still occupy the old screen position, blank it out. */
ReadConsoleOutputCharacter(hConsoleOut, &OldCell, 1, Old, &Dummy);
ReadConsoleOutputAttribute(hConsoleOut, &OldAttrib, 1, Old, &Dummy);
if ((OldCell == MyCell) && (OldAttrib == MyAttrib))
WriteConsoleOutputCharacter(hConsoleOut, &BlankCell, 1, Old, &Dummy);
/* Draw new face, then clear screen lock */
WriteConsoleOutputCharacter(hConsoleOut, &MyCell, 1, Coords, &Dummy);
WriteConsoleOutputAttribute(hConsoleOut, &MyAttrib, 1, Coords, &Dummy);
ReleaseMutex(hScreenMutex);
/* Increment the coordinates for next placement of the block. */
Old.X = Coords.X;
Old.Y = Coords.Y;
Coords.X += Delta.X;
Coords.Y += Delta.Y;
/* If we are about to go off the screen, reverse direction */
if (Coords.X < 0 || Coords.X >= csbiInfo.dwSize.X) {
Delta.X = -Delta.X;
Beep(400, 50);
}
if (Coords.Y < 0 || Coords.Y > csbiInfo.dwSize.Y) {
Delta.Y = -Delta.Y;
Beep(600, 50);
}
}
/* Repeat while RunMutex is still taken. */
while (WaitForSingleObject(hRunMutex, 75L) == WAIT_TIMEOUT);
}
void
WriteTitle(int ThreadNum)
{
char NThreadMsg[80];
sprintf(NThreadMsg, "Threads running: %02d. Press 'A' to start a thread,'Q' to quit.", ThreadNum);
SetConsoleTitle(NThreadMsg);
}
void
ClearScreen(void)
{
DWORD dummy;
COORD Home = {0, 0};
FillConsoleOutputCharacter(hConsoleOut, ' ', csbiInfo.dwSize.X * csbiInfo.dwSize.Y, Home, &dummy);
}
Βιβλιογραφία
- Ellis Horowitz
Βασικές αρχές γλωσσών προγραμματισμού.
Δεύτερη αμερικάνικη έκδοση.
σ. 311-348 Εκδόσεις Κλειδάριθμος, 1984.
- Andrew S. Tanenbaum
Σύγχρονα λειτουργικά συστήματα. σ. 39-101
Εκδόσεις Παπασωτηρίου, 1993.
Κατανεμημένος προγραμματισμός
Εισαγωγή
Σημείωση
Οι σημειώσεις της ενότητας αυτής έχουν γραφεί από τον υποψήφιο
διδάκτορα του Τμήματος Πληροφοριακών και Επικοινωνιακών Συστημάτων
του Πανεπιστημίου Αιγαίου,
Κωνσταντίνο Ράπτη (mailto:krap@aegean.gr).
Πριν δύο δεκαετίες, η φύση των υπολογιστικών συστημάτων εκφράζονταν με την ύπαρξη ισχυρών κεντρικών υπολογιστικών συστημάτων (mainframes)
στα οποία ήταν εγκατεστημένες εφαρμογές οι οποίες μπορούσαν να προσπελαστούν από τους χρήστες μέσα από τα τερματικά του συστήματος.
Με την εμφάνιση και την ανάπτυξη των προσωπικών υπολογιστών και καθώς οι εφαρμογές άρχισαν να γίνονται ολοένα και πιο μεγάλες και σύνθετες, άρχισαν να μεταμορφώνονται από ενιαία προγράμματα σε κομμάτια ικανά να συνεργάζονται μεταξύ τους.
Στην διάσπαση των εφαρμογών σε περισσότερα από ένα μέρη, συνέβαλλε η ανάπτυξη της αρχιτεκτονικής πελάτη/εξυπηρετητή (client/server)
βάσει της οποίας γινόταν η υλοποίησή τους.
Κατά την αρχιτεκτονική πελάτη/εξυπηρετητή, μία διεργασία καλείται
πελάτης (client process) όταν αιτείται την υλοποίηση κάποιων υπηρεσιών-μεθόδων από μία διεργασία η οποία είναι ικανή να της προσφέρει τις επιθυμητές υπηρεσίες.
Η τελευταία αυτή διεργασία καλείται
διεργασία του εξυπηρετητή (server process).
Αργότερα με την ανάπτυξη των υπολογιστικών δικτύων τα συνθετικά μέρη των εφαρμογών, προκειμένου να υπάρξει καλύτερη και αποτελεσματικότερη εκμετάλλευση των νέων δυνατοτήτων, άρχισαν να διαμοιράζονται στους υπολογιστές του δικτύου.
Με τον τρόπο αυτό αυξανόταν η απόδοση και εκμετάλλευση των πόρων του δικτύου.
Αυτού του είδους οι εφαρμογές ονομάζονται
κατανεμημένες εφαρμογές (distributed applications).
Η δυνατότητα διασύνδεσης, σε δίκτυα, υπολογιστικών συστημάτων διαφορετικής αρχιτεκτονικής είχε σαν αποτέλεσμα την δημιουργία ενός ανομοιογενούς υπολογιστικού περιβάλλοντος.
Θα έπρεπε, λοιπόν, και οι εφαρμογές να μπορέσουν να εξελιχθούν έτσι ώστε να είναι δυνατή η λειτουργία τους σε τέτοια ετερογενή συστήματα υπολογιστών.
Αυτή η εξέλιξη οδήγησε στην εμφάνιση των
ετερογενών κατανεμημένων εφαρμογών (heterogeneous distributed applications).
Λογισμικό βασισμένο σε εξαρτήματα
Καθώς όμως η ανάπτυξη του υλικού (hardware)
είχε ως αποτέλεσμα την δημιουργία όλο και πιο ισχυρών, ταχύτερων, μικρότερων και ταυτόχρονα φθηνότερων προϊόντων, το λογισμικό εξακολουθούσε να είναι όλο και πιο σύνθετο, πολύπλοκο και ακριβό.
Για να ξεπεραστεί αυτή η αδυναμία του λογισμικού θα έπρεπε να υπάρχει η δυνατότητα κάθε νέο λογισμικό να μην είναι απαραίτητο να αναπτυχθεί από την αρχή αλλά να δίνετε η δυνατότητα να προστίθενται νέα κομμάτια λογισμικού τα οποία θα μπορούσαν άμεσα να χρησιμοποιηθούν μέσα από το λογισμικό.
Αυτή η εξέλιξη οδήγησε στην ανάπτυξη της
τεχνολογίας των εξαρτημάτων λογισμικού (software components technology).
Η νέα αυτή τεχνολογία έχει ως σκοπό να επιτρέψει την χρησιμοποίηση κομματιών λογισμικού τα οποία θα είναι κατασκευασμένα από διαφορετικούς κατασκευαστές, θα είναι υλοποιημένα σε διαφορετικές γλώσσες προγραμματισμού και θα τρέχουν κάτω από διαφορετικά λειτουργικά συστήματα.
Τεχνολογίες διαλειτουργικότητας κομματιών λογισμικού
Συνοψίζοντας τα παραπάνω, μπορούμε τα πούμε ότι τα βασικά στοιχεία που χαρακτηρίζουν ένα σύγχρονο υπολογιστικό περιβάλλον είναι τα εξής:
- Η χρησιμοποίηση κατανεμημένων εφαρμογών οι οποίες βασίζονται στην τεχνολογία πελάτη/εξυπηρετητή,
- Η λειτουργία των εφαρμογών σε περιβάλλον το οποίο πιθανόν να αποτελείται από υπολογιστικά συστήματα τα οποία να βασίζονται σε διαφορετική τεχνολογία και να λειτουργούν "πάνω" σε διαφορετικά λειτουργικά συστήματα και
- Η χρησιμοποίηση εργαλείων προγραμματισμού (γλώσσες προγραμματισμού) τα οποία βασίζονται στην τεχνολογία του αντικειμένου.
Με βάση τα παραπάνω στοιχεία η χρησιμοποίηση μοντέλων-μεθοδολογιών τα οποία θα διευκολύνουν την λειτουργία των εφαρμογών κάτω από τις συνθήκες ενός ετερογενούς περιβάλλοντος και θα επιτελούν τις λειτουργίες επικοινωνίας με τρόπο τέτοιο έτσι ώστε να είναι διαφανής προς τους τελικούς χρήστες, είναι επιβεβλημένη και η υιοθέτησή τους αποτελεί πλέον ζήτημα για τους οργανισμούς και τις επιχειρήσεις οι οποίες συντηρούν τέτοιου είδους υπολογιστικά συστήματα και δίκτυα.
Τρία είναι τα βασικά μοντέλα των οποίων σκοπός είναι η υλοποίηση των παραπάνω και αυτά είναι:
- Η "CORBA" (Common Object Request Broker Architecture) της Object Management Group (OMG).
- H "COM/DCOM" (Component Object Model / Distributed Component Object Model) της Microsoft.
- Η "Java RMI" (Remote Method Invocation) της Sun.
Και τα τρία αυτά μοντέλα βασίζονται πάνω στις ίδιες αρχές (και οι τρεις τεχνολογίες λειτουργούν βασιζόμενες στην έννοια του αντικειμένου) και εξυπηρετούν τον ίδιο σκοπό. Κάθε ένα από αυτά έχει όμως τις δικές του ιδιαιτερότητες από τις οποίες κρίνονται και επιλέγονται.
OMG CORBA
Η OMG (Object Management Group) αναπτύσσει πρότυπα βασιζόμενη στην τεχνολογία του αντικειμένου.
Σύμφωνα με την αρχιτεκτονική που προτείνεται από την OMG, την OMA (Object Management Architecture), το κάθε κομμάτι λογισμικού παρουσιάζεται σαν αντικείμενο (Object).
Τα διάφορα αντικείμενα επικοινωνούν μεταξύ τους μέσω του ORB (Object Request Broker), ο οποίος αποτελεί το στοιχείο-κλειδί γι' αυτήν την επικοινωνία.
Ο ORB παρέχει τους μηχανισμούς εκείνους μέσω των οποίων τα αντικείμενα κάνουν τις αιτήσεις και συλλέγουν τις αποκρίσεις.
Η CORBA (Common Object Request Broker Architecture) αποτελεί το πρότυπο της OMG για τον ORB.
Κατά την CORBA, ένας πελάτης ο οποίος ζητάει κάποιες υπηρεσίες από κάποιο αντικείμενο, κάνει μία αίτηση (request)
η οποία μεταβιβάζεται στον ORB και ο οποίος είναι στη συνέχεια υπεύθυνος για την προώθηση της αίτησης προς τον κατάλληλο εξυπηρετητή.
Η αίτηση αυτή περιέχει όλες τις απαραίτητες πληροφορίες που απαιτούνται προκειμένου να υλοποιηθεί και αυτές είναι:
- το επιθυμητό αντικείμενο,
- επιθυμητές υπηρεσίες,
- τυχόν παραμέτρους.
Για να είναι κατανοητή όμως η αίτηση θα πρέπει να έχει μία κατάλληλη μορφή και γι'
αυτό το λόγο γίνεται προς τον ORB μέσω διασυνδετών (interfaces: "Dynamic Invocation" και "IDL Stubs").
Για τον ίδιο λόγο η προώθηση της αίτησης προς τον εξυπηρετητή γίνεται μέσω διασυνδετών ("Static IDL Skeleton" και "IDL Skeleton").
Ένας διασυνδέτης αποτελεί μία περιγραφή ενός συνόλου από πιθανές λειτουργίες τις οποίες ένας πελάτης μπορεί να αιτηθεί ενός αντικειμένου.
Ένα αντικείμενο ικανοποιεί έναν διασυνδέτη εάν μπορεί να προσδιοριστεί σαν το αντικείμενο-στόχος (target object) για κάθε δυνατή αίτηση η οποία περιγράφεται από τον διασυνδέτη.
Στο σημείο αυτό δεν θα πρέπει να αγνοήσουμε την ιδιότητα της κληρονομικότητας που χαρακτηρίζει τους διασυνδέτες και η οποία παρέχει τον συνθετικό εκείνο μηχανισμό που επιτρέπει σ' ένα αντικείμενο να υποστηρίζει πολλαπλούς διασυνδέτες.
Οι διασυνδέτες καθορίζονται πλήρως μέσω της OMG IDL (Interface Definition Language).
Η IDL δεν αποτελεί γλώσσα προγραμματισμού, όπως οι γνωστές γλώσσες προγραμματισμού, αλλά μία καθαρά περιγραφική γλώσσα η οποία δεν περιλαμβάνει δομές αλγορίθμων ή μεταβλητές.
Η γραμματική της αποτελεί υποσύνολο της ANSI C++ με πρόσθετες κατασκευές για την υποστήριξη μηχανισμών για την επίκληση απομακρυσμένων λειτουργιών.
Οι πελάτες δεν είναι "γραμμένοι" σε OMG IDL αλλά σε γλώσσα για την οποία έχει προσδιοριστεί η αντιστοιχία της με την OMG IDL.
Microsoft COM/DCOM
Το δεύτερο μοντέλο βασίζεται και αυτό στην τεχνολογία του αντικειμένου με την έννοια όμως εδώ των αυτόνομων συνθετικών εξαρτημάτων μίας εφαρμογής.
Και εδώ, ο βασικός σκοπός του μοντέλου είναι να καταστήσει δυνατή την επικοινωνία δύο ή περισσοτέρων εφαρμογών ή συνθετικών, ακόμα και στην περίπτωση που αυτά διαφέρουν ως προς την προέλευση, τη γλώσσα προγραμματισμού, τα μηχανήματα στα οποία βρίσκονται και τα λειτουργικά συστήματα κάτω από τα οποία τρέχουν.
Ο "μηχανισμός λειτουργίας του μοντέλου είναι παρόμοιος με αυτόν της CORBA. Βασικό ρόλο παίζουν οι διασυνδέτες τα οποία δεν είναι τίποτα άλλο από ένα σαφές διατυπωμένο "συμβόλαιο" μεταξύ των "κομματιών" λογισμικού, το οποίο περιέχει ένα σύνολο από σχετικές λειτουργίες.
Όταν λέμε ότι ένα αντικείμενο υλοποιεί έναν διασυνδέτη αυτό σημαίνει ότι το αντικείμενο αυτό υλοποιεί κάθε λειτουργία του.
Πως γίνεται, όμως, η διαδικασία της αιτήσεως κάποιων λειτουργιών από έναν πελάτη;
Για να μπορέσει ένας πελάτης να εξυπηρετηθεί από κάποιο αντικείμενο, θα πρέπει η γλώσσα προγραμματισμού, στην οποία είναι υλοποιημένος, να έχει τη δυνατότητα δημιουργίας δεικτών και να καλεί συναρτήσεις μέσω των δεικτών. Θα πρέπει λοιπόν ο πελάτης να έχει τον κατάλληλο δείκτη προς τον επιθυμητό διασυνδέτη τον οποίο περιέχει τις επιθυμητές από τον πελάτη λειτουργίες.
Στην περίπτωση όμως που ο πελάτης δεν έχει τον κατάλληλο δείκτη προς το επιθυμητό διασυνδέτη, τότε απευθύνεται προς την COM δίνοντας σαν στοιχεία την
ταυτότητα της κλάσης του εξυπηρετητή που επιθυμεί ο πελάτης και τον τύπο του,
δηλαδή εάν είναι:
Η COM τότε αναλαμβάνει να βρει τον επιθυμητό εξυπηρετητή και να επιστρέψει στον πελάτη τον επιθυμητό δείκτη.
Όπως έγινε σαφές, ο κάθε πελάτης μπορεί να είναι υλοποιημένος σε οποιαδήποτε γλώσσα προγραμματισμού, αρκεί αυτή να υποστηρίζει την κατασκευή και την χρήση δεικτών. Για τα "interfaces", όμως, υπάρχει και εδώ μία "IDL" (interface Definition Language) η οποία επιτρέπει και βοηθάει τους σχεδιαστές να κατασκευάζουν διασυνδέτες.
Sun Java RMI
Το μοντέλο RMI της Sun, βασίζεται και αυτό στην τεχνολογία του αντικειμένου και είναι σχεδιασμένο για να προάγει την διαλειτουργικότητα μεταξύ αντικειμένων, κατασκευασμένων σε Java, μέσα σε ένα κατανεμημένο και ετερογενές περιβάλλον.
Η τεχνολογία RMI αφορά αποκλειστικά αντικείμενα τα οποία είναι κατασκευασμένα με τη γλώσσα προγραμματισμού Java. Αυτό έχει ως αποτέλεσμα να δίνει την δυνατότητα της πλήρους εκμετάλλευσης των δυνατοτήτων της Java αλλά και το πλεονέκτημα του ομοιογενούς, ως προς τη γλώσσα προγραμματισμού, περιβάλλοντος.
Η αρχιτεκτονική ενός RMI συστήματος ακολουθεί την δομή των στρωμάτων-επιπέδων (layers). Υπάρχουν τρία (συν ένα) επίπεδα:
- Το επίπεδο "Stub/Skeleton".
- Το επίπεδο απομακρυσμένης αναφοράς (Remote Reference).
- Το επίπεδο μεταφοράς (Transport)
Υπάρχει, επίσης, το επίπεδο της Εφαρμογής (Application) το οποίο όμως δεν αποτελεί καθαρό κομμάτι της τεχνολογίας και το οποίο βρίσκεται πάνω απΤ τα υπόλοιπα.
Η διαδικασία επίκλησης κάποιων υπηρεσιών από έναν πελάτη, δεν διαφέρει, ιδιαίτερα, σε σχέση με τις προαναφερθείσες τεχνολογίες. Ένας πελάτης, ο οποίος επιθυμεί την υλοποίηση κάποιων υπηρεσιών, υποβάλλει μία αίτηση προς το RMI-σύστημα. Στην αίτηση αυτή θα πρέπει να αναφέρονται οι κατάλληλες εκείνες πληροφορίες οι οποίες απαιτούνται για την αποστολή της αίτησης προς τον κατάλληλο εξυπηρετητή. Αυτές οι πληροφορίες-παράμετροι είναι: μία αναφορά του επιθυμητού αντικειμένου (object reference), οι επιθυμητές μέθοδοι και οι κατάλληλες παράμετροι για την υλοποίηση των μεθόδων.
Από τη στιγμή που ο πελάτης καταθέσει την αίτησή του, το RMI-σύστημα είναι υπεύθυνο για την ανεύρεση του κατάλληλου εξυπηρετητή, την μεταβίβαση της αιτήσεων και την επιστροφή των αποτελεσμάτων στον πελάτη.
Όμοια με την CORBA και την COM/DCOM, σε ένα RMI-σύστημα, οι πελάτες δεν έρχονται ποτέ σε απευθείας επικοινωνία με τα επιθυμητά αντικείμενα παρά μόνο μέσω των διασυνδετών αυτών των αντικειμένων. Και εδώ ο διασυνδέτης είναι αυτός που περιέχει τις μεθόδους-υπηρεσίες τις οποίες μπορεί να υλοποιήσει το αντικείμενο.
Η επικοινωνία πελάτη-αντικειμένου, σΤ ένα σύστημα RMI, είναι η ίδια ανεξάρτητα με το που βρίσκεται το επιθυμητό αντικείμενο (αν δηλαδή βρίσκεται στην ίδια διεργασία με τον πελάτη, αν βρίσκεται τοπικά ή απομακρυσμένα).
Πηγές για έρευνα στο Internet
Βιβλιογραφία
- M. Ben-Ari.
Principles of Concurrent and Distributed Programming.
Prentice-Hall, 1990.
- Peter
Duchessi and InduShobha Chengalur-Smith.
Client/server benefits, problems, best practices.
Communications of the ACM, 41(5):87–94, May 1998.
- David Flanagan.
Java
in a Nutshell.
O'Reilly and Associates, Sebastopol, CA, USA, 1997.
- Scott M. Lewandowski.
Frameworks for component-based client/server computing.
ACM Computing Surveys, 30(1):3–27, March 1998.
- Sape Mullender, editor.
Distributed Systems.
Addison-Wesley, 1989.
- Oscar Nierstrasz,
Simon Gibbs, and Dennis Tsichritzis.
Component-oriented software development.
Communications of the ACM, 35(9):160–165, September 1992.
- Jeffrey M. Voas.
The challenges of using COTS software in component-based development.
Computer, 31(6):44–45, June 1998.
Θέματα εξετάσεων και άλλες σχετικές πληροφορίες
Εξεταστική περιόδος Ιουνίου 2000
ΠΑΝΕΠΙΣΤΗΜΙΟ
ΑΙΓΑΙΟΥ
Τμήμα
Πληροφοριακών
και Επικοινωνιακών
Συστημάτων
Γλώσσες προγραμματισμού
Διδάσκων: Επικ. Καθηγητής Διομήδης Σπινέλλης
| Εξεταστική περίοδος
Ιουνίου 2000
|
Μια οικολογική οργάνωση σας ζητά να υλοποιήσετε ένα πρόγραμμα για την παρακολούθηση της πανίδας της Ελλάδας. Το πρόγραμμα θα περιλαμβάνει στοιχεία για κάθε ξεχωριστό οργανισμό (π.χ. έναν συγκεκριμένο αετό). Τα στοιχεία αυτά είναι: κωδικός αριθμός, γεωγραφικές συντεταγμένες που έχει εμφανιστεί τελευταία φορά (γεωγραφικό μήκος και πλάτος) και συνολική απόσταση που εμφανίζεται να έχει διανύσει. Η συνολική απόσταση πρέπει να ενημερώνεται αυτόματα κάθε φορά που καταχωρούνται νέες συντεταγμένες. Επίσης, για κάθε κατηγορία οργανισμών (π.χ. για όλους τους αετούς) πρέπει να τηρείται αυτόματα ο αριθμός των οργανισμών που παρακολουθούνται. Ειδικά για τους θαλάσσιους οργανισμούς θα φυλάσσεται το μέγιστο βάθος στο οποίο έχει εμφανιστεί ένας συγκεκριμένος οργανισμός και για τα πτηνά το αντίστοιχο μέγιστο ύψος. Επίσης, για κάθε δελφίνι θα πρέπει να φυλάσσεται ο αριθμός των παιδιών του.
Θέμα 1ο: (2.5 βαθμοί)
Σχεδιάστε σε UML ένα διάγραμμα κλάσεων για το παραπάνω πρόγραμμα. Στο διάγραμμα πρέπει να φαίνονται οι ιδιότητες καθώς και μέθοδοι που εσείς κρίνετε απαραίτητες. Σχεδιάστε ένα αντιπροσωπευτικό διάγραμμα αντικειμένων.
Θέμα 2ο: (2.5 βαθμοί)
Να γράψετε σε C++ μια κλάση που να υλοποιεί τις γεωγραφικές συντεταγμένες. Η κλάση να επανακαθορίζει τον τελεστή - έτσι ώστε να επιστρέφει την απόσταση ανάμεσα σε δύο σημεία.
Σημείωση: θεωρήσετε πως οι γεωγραφικές συντεταγμένες καταχωρούνται σε μέτρα και πως η απόσταση μεταξύ δύο σημείων μπορεί να υπολογιστεί ως η υποτείνουσα του αντίστοιχου τριγώνου.
Θέμα 3ο: (2.5 βαθμοί)
Γράψτε σε C++ τις δηλώσεις για τις κλάσεις που απαιτούνται για το πρόγραμμα παρακολούθησης της πανίδας καθώς και τους ορισμούς των συναρτήσεων κατασκευής και της συνάρτησης που ενημερώνει τις συντεταγμένες της τελευταίας εμφάνισης. Αν τα στοιχεία για τους οργανισμούς φυλάσσονται σε έναν πίνακα με δείκτες στα αντίστοιχα αντικείμενα, γράψτε μια συνάρτηση που να επιστρέφει τη συνολική απόσταση που έχει διανυθεί από όλους τους καταχωρημένους οργανισμούς.
Θέμα 4ο: (2.5 βαθμοί)
Τα παρακάτω κατηγορήματα της Prolog ορίζουν αναδρομικά μια οικογένεια δελφινιών:
familydolphin(d).
familydolphin(child(X)) :-
familydolphin(X).
Να ορίσετε, βασισμένοι στα παραπάνω, το κατηγόρημα familygen(X, Y)
που είναι αληθές όταν και μόνο όταν το Υ είναι ο αριθμός των γενεών της
οικογένειας X. Για παράδειγμα το κατηγόρημα
familygen(child(child(d)), 2) είναι αληθές
(η οικογένεια X είναι δύο γενεών).
Διάρκεια εξέτασης 2,5 ώρες
| Καλή επιτυχία!
|
Εξεταστική περιόδος Σεπτεμβρίου 2000
ΠΑΝΕΠΙΣΤΗΜΙΟ
ΑΙΓΑΙΟΥ
Τμήμα
Πληροφοριακών
και Επικοινωνιακών
Συστημάτων
Γλώσσες προγραμματισμού
Διδάσκων: Επικ. Καθηγητής Διομήδης Σπινέλλης
| Εξεταστική περίοδος
Σεπτεμβρίου 2000
|
Ο οργανισμός "Αθήνα 2004" σας αναθέτει να υλοποιήσετε ένα δοκιμαστικό πρόγραμμα για την παρακολούθηση των αθλητών των Ολυμπιακών αγώνων. Το πρόγραμμα θα περιλαμβάνει στοιχεία για κάθε ξεχωριστό αθλητή. Τα στοιχεία αυτά είναι: όνομα, εθνικότητα, άθλημα που αγωνίζεται, στοιχεία σχετικές με τις προσπάθειές του στο άθλημα και η καλύτερη επίδοση στο άθλημα αυτό. Κάθε αθλητής λαμβάνει μέρος σε ένα και μόνο ένα άθλημα. Για κάθε άθλημα πρέπει να τηρείται αυτόματα ο αριθμός των αθλητών που αγωνίζονται. Στα αθλήματα ταχύτητας φυλάσσεται η επίδοση (χρόνος) ως αριθμός κινητής υποδιαστολής, στα άλματα φυλάσσεται η απόσταση ως αριθμός κινητής υποδιαστολής, και στις άρσεις φυλάσσεται το βάρος ως ακέραιος αριθμός. Σε ορισμένα αθλήματα πρέπει να φυλάσσεται και ο αριθμός των προσπαθειών. Το πρόγραμμα πρέπει να μπορεί να εμφανίζει τα στοιχεία για έναν συγκεκριμένο αθλητή, να ενημερώνει τα στοιχεία του αθλητή με τα αποτελέσματα από μια προσπάθεια και να εμφανίζει τους αθλητές για ένα άθλημα με τη σωστή σειρά κατάταξης.
Θέμα 1ο: (2 βαθμοί)
Σχεδιάστε σε UML ένα διάγραμμα κλάσεων για το παραπάνω πρόγραμμα. Στο διάγραμμα πρέπει να φαίνονται οι ιδιότητες καθώς και οι μέθοδοι που εσείς κρίνετε απαραίτητες. Σχεδιάστε ένα αντιπροσωπευτικό διάγραμμα αντικειμένων.
Θέμα 2ο: (2 βαθμοί)
Για κάθε άθλημα να γράψετε σε C++ τη μέθοδο compare(a1, a2) που επιστρέφει -1, 0, ή 1 ανάλογα με το αν η επίδoση του αθλητή a1 είναι καλύτερη, ίση ή χειρότερη με την επίδοση του αθλητή a2.
Θέμα 3ο: (2 βαθμοί)
Να γράψετε σε C++ μια κλάση που να υλοποιεί τις "προσπάθειες". Η κλάση να έχει συνάρτηση κατασκευής που να λαμβάνει ως όρισμα τον αριθμό των προσπαθειών που επιτρέπει ένα αγώνισμα και τις μεθόδους NewTry που εκτελείται πριν από κάθε προσπάθεια και εμφανίζει τον αριθμό της και αυτές που απομένουν και την MoreTries που επιστρέφει αληθές αν επιτρέπονται άλλες προσπάθειες.
Θέμα 4ο: (2 βαθμοί)
Γράψτε σε C++ μια μέθοδο για την κλάση του αθλητή που λαμβάνει ως όρισμα έναν πίνακα με αθλητές που αγωνίστηκαν σε ένα αγώνισμα, και τον αριθμό τους. Η μέθοδος πρέπει με τη χρήση της compare να εμφανίζει τον αθλητή που έχει την καλύτερη επίδοση στο συγκεκριμένο αγώνισμα.
Θέμα 5ο: (2 βαθμοί)
Η παρακάτω συνάρτηση της Lisp επιστρέφει τον αριθμό των στοιχείων μιας λίστας:
(defun length (a)
(if (null a)
0
(+ (length (cdr a)) 1)))
Αν η συνάρτηση listp είναι αληθής όταν το όρισμά της είναι λίστα και η car επιστρέφει το πρώτο στοιχείο μιας λίστας, να ορίσετε μια συνάρτηση expandlength που να επιστρέφει τον αριθμό των στοιχείων μια λίστας λαμβάνοντας υπόψη και τα στοιχεία της λίστας που και αυτά είναι λίστες. Για παράδειγμα ενώ η (length '(5 8 40 '(60 90))) έχει την τιμή 4 η (expandlength '(5 8 40 '(60 90))) έχει την τιμή 5.
Διάρκεια εξέτασης 2,5 ώρες
| Καλή επιτυχία!
|
Βοηθήματα στις εξετάσεις
Στην εξέταση για το μάθημα Γλώσσες προγραμματισμού
οι εξεταζόμενοι μπορούν (προαιρετικά) να έχουν μαζί τους ένα φύλλο με σημειώσεις σύμφωνα με τους παρακάτω όρους:
- Οι σημειώσεις πρέπει να είναι όλες γραμμένες σε ένα φύλλο μεγέθους Α4.
- Επιτρέπεται η χρήση και των δύο όψεων του φύλλου.
- Το φύλλο παραδίδεται μαζί με το γραπτό.
- Οι πρώτες λέξεις και στις δύο όψεις του φύλλου πρέπει να είναι το ονοματεπώνυμο και ο αριθμός μητρώου του εξεταζόμενου.
- Οι σημειώσεις πρέπει να είναι γραμμένες στα ελληνικά ή αγγλικά, να διαβάζονται με γυμνό μάτι και να μην είναι κωδικοποιημένες.
- Οι σημειώσεις επιτρέπεται να περιέχουν κώδικα στις γλώσσες προγραμματισμού που έχουν διδαχτεί στο μάθημα καθώς και σχήματα UML.
- Στο τέλος της δεύτερης όψης πρέπει να υπάρχει η παρακάτω φράση
Η σημειώσεις στο φύλλο αυτό έχουν συνταχθεί από εμένα.
Κανένα τμήμα τους δεν έχει αντιγραφεί αυτούσιο από άλλη πηγή.
μαζί με το ονοματεπώνυμο, την ημερομηνία και υπογραφή του εξεταζόμενου.
- Όλα τα περιεχόμενα του φύλλου (με εξαίρεση την υπογραφή) πρέπει να είναι τυπωμένα.
- Απαγορεύονται οι χειρόγραφες προσθήκες ή σημειώσεις στο φύλλο, τόσο πριν, όσο και κατά τη διάρκεια της εξέτασης.
- Παράβαση των παραπάνω όρων συνεπάγεται μηδενισμό του γραπτού.