Χρήστος Κοίλιας Δομές Δεδομένων και Οργανώσεις Αρχείων. Εκδόσεις Νέων Τεχνολογιών, 1993.
/* * Abstract Data Type Definition file point.h */ typedef struct s_point *point; point new_point(void); int get_x_point(point p); int get_y_point(point p); void set_point(point p, int x, int y); void delete_point(point p);
/* * Abstract Data Type Implementation file point.c */ #include <stdlib.h> #include "point.h" struct s_point { int x, y; /* Point coordinates */ }; point new_point(void) { return (point)malloc(sizeof(struct s_point)); } int get_x_point(point p) { return (p->x); } int get_y_point(point p); { return (p->y); } void set_point(point p, int x, int y) { p->x = x; p->y = y; } void delete_point(point p) { free(p); }
Σημείωση: στη γλώσσα C++ οι μέθοδοι new και delete παρέχονται από τη γλώσσα, ενώ στις υπόλοιπες συναρτήσεις ο δείκτης p μεταφέρεται αυτόματα
Το παρακάτω παράδειγμα ορίζει έναν ΑΤΔ για πίνακα ακεραίων δύο διαστάσεων με δυναμικά οριζόμενες διαστάσεις. Ο υπολογισμός της θέσης ενός στοιχείου γίνεται με τη απεικόνιση των δύο διαστάσεων του πίνακα στη μία διάσταση της δυναμικής μνήμης.
/* * Abstract Data Type Definition file array2d.h */ typedef struct s_array2d *array2d; array2d new_array2d(int rows, int cols); void array2d_set_value(array2d a, int row, int col, int val); int array2d_get_value(array2d a, int row, int col); void array2d_delete(array2d a);
/* * Abstract Data Type Implementation file array2d.c * 2D -> 1D mapping implementation */ #include <stdlib.h> #include <assert.h> #include "array2d.h" struct s_array2d { int *values; /* Value storage */ int rows, cols; /* Dimensions */ }; array2d new_array2d(int rows, int cols) { array2d a; a = (array2d)malloc(sizeof(struct s_array2d)); assert(a != NULL); a->values = (int *)malloc(rows * cols * sizeof(int)); assert(a->values != NULL); a->rows = rows; a->cols = cols; } void array2d_set_value(array2d a, int row, int col, int val) { assert(row > 0 && row < a->rows); assert(col > 0 && col < a->cols); a->values[row * a->cols + col] = val; } int array2d_get_value(array2d a, int row, int col) { assert(row > 0 && row < a->rows); assert(col > 0 && col < a->cols); return (a->values[row * a->cols + col]); } void array2d_delete(array2d a) { free(a->values); free(a); }
Εναλλακτικά, μπορούμε να υλοποιήσουμε τον πίνακα δύο διαστάσεων με βάση έναν πίνακα από δείκτες σε πίνακες μιας διάστασης. Η τεχνική αυτή υλοποίησης μπορεί να είναι αποδοτικότερη σε αρχιτεκτονικές όπου ο πολλαπλασιασμός διαρκεί μεγαλύτερο χρόνο από την πρόσβαση στη μνήμη ή σε περιπτώσεις όπου οι διαστάσεις των γραμμών του πίνακα δεν είναι όλες ίσες.
Οι δηλώσεις του ΑΤΔ παραμένουν φυσικά οι ίδιες.
/* * Abstract Data Type Implementation file array2d.c * Array of pointer implementation */ #include <stdlib.h> #include <assert.h> #include "array2d.h" struct s_array2d { int **values; /* Value storage */ int rows, cols; /* Dimensions */ }; array2d new_array2d(int rows, int cols) { array2d a; int i; a = (array2d)malloc(sizeof(struct s_array2d)); assert(a != NULL); a->values = (int **)malloc(rows * sizeof(int *)); assert(a->values != NULL); for (i = 0; i < rows; i++) { a->values[i] = (int *)malloc(cols * sizeof(int)); assert(a->values[i] != NULL); } a->rows = rows; a->cols = cols; } void array2d_set_value(array2d a, int row, int col, int val) { assert(row > 0 && row < a->rows); assert(col > 0 && col < a->cols); a->values[row][col] = val; } int array2d_get_value(array2d a, int row, int col) { assert(row > 0 && row < a->rows); assert(col > 0 && col < a->cols); return (a->values[row][col]); } void array2d_delete(array2d a) { int i; for (i = 0; i < a->rows; i++) free(a->values[i]); free(a->values); free(a); }
/* * Abstract Data Type Implementation file array2d.c * Array of pointer implementation * Special case for symmetric arrays */ #include <stdlib.h> #include <assert.h> #include "sym.h" struct s_array2d { int **values; /* Value storage */ int rows, cols; /* Dimensions */ }; /* * Given a pointer to a symmetric array and the coordinates of an * an element return a pointer to its location. Elements on the left * and below the diagonal are mapped using the diagonal as the axis of * symmetry */ static int * value_position(array2d a, int row, int col) { assert(row >= 0 && row < a->rows); assert(col >= 0 && col < a->cols); if (row > col) return (&(a->values[col][row])); else return (&(a->values[row][col])); } array2d new_array2d(int rows, int cols) { array2d a; int i; assert(rows == cols); /* Must be symmetric */ a = (array2d)malloc(sizeof(struct s_array2d)); assert(a != NULL); a->values = (int **)malloc(rows * sizeof(int *)); assert(a->values != NULL); for (i = 0; i < rows; i++) { a->values[i] = (int *)malloc((i + 1) * sizeof(int)); assert(a->values[i] != NULL); } a->rows = rows; a->cols = cols; return (a); } void set_val_array2d(array2d a, int row, int col, int val) { *value_position(a, row, col) = val; } int get_val_array2d(array2d a, int row, int col) { return (*value_position(a, row, col)); } void delete_array2d(array2d a) { int i; for (i = 0; i < a->rows; i++) free(a->values[i]); free(a->values); free(a); }
$rental_price{"Car category A"} = 12000; $rental_price{"Car category B"} = 14000; $rental_price{"Car category D"} = 16000; $rental_price{"Car category R"} = 20000; print $rental_price{"Car category R"};
/* List of integers */ struct s_list { int val; /* Integer value */ struct s_list *next; };
NULL
.
malloc
.
struct s_list *head, *p; p = (struct s_list *)malloc(sizeof(struct s_list)); p->next = head; /* Set other members of p */ head = p;
for (p = head; p; p = p->next) /* Process element p */
free(p); p = p->next;Σωστό:
tmp = p->next; free(p); p = tmp;
#include <stdlib.h> #include <stdio.h> /* List of integers */ struct s_list { int val; /* Integer value */ struct s_list *next; }; main() { struct s_list *head, *p; int n; head = NULL; /* Read list elements */ while (scanf("%d", &n) == 1) { p = (struct s_list *)malloc(sizeof(struct s_list)); p->val = n; p->next = head; head = p; } printf("\nStarting output:\n"); /* Print list elements */ for (p = head; p; p = p->next) printf("%d\n", p->val); }
typedef struct s_int_list *int_list;
int_list list_new(void);
int_list int_list_add(int_list l, int i);
int_list int_list_del(int_list l, int i);
int int_list_search(int_list l, int i);
void int_list_traverse(int_list l, void (*process)(int i));
int int_list_isempty(int_list l);
void int_list_delete(int_list l);
struct s_int_dlist { int val; /* Integer value */ struct s_int_dlist *prev; /* Previous element */ struct s_int_dlist *next; /* Next element */ };Η εισαγωγή ενός στοιχείου np πριν από το στοιχείο της λίστας p γίνεται με τις παρακάτω εντολές:
p->prev->next = np; np->next = p; np->prev = p->prev; p->prev = np;
struct s_dlist *start, *p; p = start; if (p) do { /* Process list element */ ... p = p->next; } while (p != start);
char_list char_list_new(void);
char_list char_list_add(char_list l, char c);
void char_list_traverse(char_list l, void (*process)(char c));
int char_list_isempty(char_list l);
void char_list_delete(char_list l);
L I V E . E V I L
struct s_int_queue { int values[20]; /* Array of values */ int head; /* Head of queue (values[head]) */ int tail; /* Tail of queue (values[tail]) */ };
struct s_int_queue { int *values; /* Allocated array of values */ int values_size; /* Number of values allocated */ int head; /* Head of queue (values[head]) */ int tail; /* Tail of queue (values[tail]) */ };
struct s_int_queue { int *values_start; /* Start of allocated array of values */ int *values_end; /* End of allocated array of values */ int *head; /* Head of queue */ int *tail; /* Tail of queue */ };
struct s_int_list { int val; struct s_int_list *next; }; struct s_int_queue { struct s_int_list *head; struct s_int_list *tail; }; /* Add an element to the queue */ void int_queue_put(struct s_int_queue *q, int v) { struct s_int_list *p; p = (struct s_int_list *)malloc(sizeof(struct s_int_list)); p->next = NULL; p->val = v; if (q->tail != NULL) q->tail->next = p; /* Add element to queue tail */ else q->head = p; /* Special case for empty queue */ q->tail = p; } /* Remove and return an element from a non-empty queue */ int int_queue_get(struct s_int_queue *q) { int v; struct s_int_list *tmp; assert(q->head != NULL); v = q->head->val; if (q->head->next == NULL) q->tail = NULL; /* Special case for empty queue */ tmp = q->head->next; free(q->head); q->head = tmp; return (v); }
typedef struct s_int_queue *int_queue;
int_queue new_int_queue(void);
void put_int_queue(int_queue q, int i);
int get_int_queue(int_queue q);
int isempty_int_queue(int_queue q);
void delete_int_queue(int_queue q);
int_queue new_int_queue(void);
void put_int_queue(int_queue q, int i);
int get_int_queue(int_queue q);
int isempty_int_queue(int_queue q);
I 1 I 2 I 3 O 1 I 4 O 2 ...
NULL
.
malloc
.
#include <stdlib.h> /* * Declare a binary tree of integers */ struct s_tree { int val; struct s_tree *left, *right; }; main() { struct s_tree *tree, *node; node = (struct s_tree *)malloc(sizeof(struct s_tree)); node->val = 5; node->left = node->right = NULL; tree = node; node = (struct s_tree *)malloc(sizeof(struct s_tree)); node->val = 12; node->left = node->right = NULL; tree->right = node; }
/* * Search for the integer i in the ordered binary tree t * If found return its node; otherwise return NULL */ struct s_tree search(struct s_tree *t, int i) { if (t == NULL) return (NULL); else if (t->val == i) return (t); else if (i < t->val) return (search(t->left, i)); else return (search(t->right, i)); }
Οι αντίστοιχες διασχίσεις διάσχισης υλοποιούνται αναδρομικά ως εξής:
void visit(struct s_tree *t, void(*process)(int val)) { if (t == NULL) return; process(t->val); visit(t->left); visit(t->right); }
void visit(struct s_tree *t, void(*process)(int val)) { if (t == NULL) return; visit(t->left); visit(t->right); process(t->val); }
void visit(struct s_tree *t, void(*process)(int val)) { if (t == NULL) return; visit(t->left); process(t->val); visit(t->right); }
/* * Process all nodes at taget level given a tree t and its current depth level * Return the number of nodes processed * (Used by the visit function) */ static int visit_level(struct s_tree *t, int current_level, int target_level, void(*process)(int val)) { if (t == NULL || current_level > target_level) return (0); else if (current_level == target_level) { process(t->val); return (1); } else return ( visit_level(t->left, current_level + 1, target_level, process) + visit_level(t->left, current_level + 1, target_level, process)); } void visit(struct s_tree *t, void(*process)(int val)) { int i = 0; int nodes_processed; do { nodes_processed = visit_level(t, 0, i, process); i++; } while (nodes_processed > 0); }
Verdi Strauss Mozart Wagner Adams Gounod Bellini Gauss Bizet Donizetti Mascagni Puccini Rossini ^Z Adams Bellini Bizet Donizetti Gauss Gounod Mascagni Mozart Puccini Rossini Strauss Verdi WagnerΣημείωση 1: Η συνάρτηση strcmp συγκρίνει αλφαβητικά δύο συμβολοσειρές (α, β), και επιστρέφει ανάλογα -1 (α < β), 0 (α = β), ή 1 (α > β). Ορίζεται στην string.h.
Σημείωση 2: Ανάγνωση των συμβολοσειρών μέχρι το τέλος του αρχείου, αντιγραφή τους σε χώρο που έχει επιστραφεί από τη malloc, και αποθήκευσή του δείκτη σε μεταβλητή p τύπου char * μπορεί να γίνει ως εξής:
char buff[80]; while (fgets(buff, sizeof(buff), stdin)) { p = strdup(buff); ... }Η δήλωση της strdup βρίσκεται στην επικεφαλίδα string.h.
Σημείωση 3: Ο ορισμός των παρακάτω δύο συναρτήσεων μπορεί να σας βοηθήσει στη δομή του προγράμματος:
static void add_tree(struct s_btree **t, char *name); static void print_tree(struct s_btree *t);
Παράδειγμα για γράφο 100 κόμβων:
/* * Adjacency matrix for a graph of 100 nodes */ static int adjmatrix[100][100]; main() { int a, b; /* Read edges and connect them */ while (scanf(%d %d", &a, &b) == 2) adjmatrix[a][b] = adjmatrix[b][a] = 1; }
Παράδειγμα για γράφο 10 κόμβων:
#include <stdlib.h> #include <stdio.h> /* * Adjacency list for a graph of 10 nodes */ struct s_adjlist { int node; struct s_adjlist *next; }; static struct s_adjlist *adj[10]; main() { int a, b; struct s_adjlist *p; int i; /* Read edges and connect them */ while (scanf("%d %d", &a, &b) == 2) { p = (struct s_adjlist *)malloc(sizeof(struct s_adjlist)); p->node = b; p->next = adj[a]; adj[a] = p; p = (struct s_adjlist *)malloc(sizeof(struct s_adjlist)); p->node = a; p->next = adj[b]; adj[b] = p; } }
#include <stdlib.h> #include <stdio.h> /* * Adjacency list for a graph of 10 nodes */ struct s_adjlist { int node; struct s_adjlist *next; }; static struct s_adjlist *adj[10]; static int visited[10]; /* * Visit the nodes starting from node printing their value using * depth first search */ static void visit(int node) { struct s_adjlist *p; visited[node] = 1; printf("%d\n", node); for (p = adj[node]; p != NULL; p = p->next) if (!visited[p->node]) visit(p->node); } main() { int a, b; struct s_adjlist *p; int i; /* Read edges and connect them */ while (scanf("%d %d", &a, &b) == 2) { p = (struct s_adjlist *)malloc(sizeof(struct s_adjlist)); p->node = b; p->next = adj[a]; adj[a] = p; p = (struct s_adjlist *)malloc(sizeof(struct s_adjlist)); p->node = a; p->next = adj[b]; adj[b] = p; } /* Depth first search/print of the graph */ for (i = 0; i < 10; i++) visited[i] = 0; for (i = 0; i < 10; i++) if (!visited[i]) visit(i); }Αν το πρόγραμμα διαβάσει το γράφο:
2 3 3 8 8 6 4 9θα τυπώσει την παρακάτω σειρά επίσκεψης:
0 1 2 3 8 6 4 9 5 7
#include <stdlib.h> void qsort(void *base, size_t num, size_t width, int (*compare)(const void *elem1, const void *elem2));
#include <stdlib.h> #include <stdio.h> /* * Integer compare function (passed to qsort) */ static int icmp(const void *arg1, const void *arg2) { return (*(int *)arg1 - *(int *)arg2); } main() { int ivals[] = {100, 5, 6, 2, 8, 3, 45, 23}; int i; qsort((void *)ivals, sizeof(ivals) / sizeof(int), sizeof(int), icmp); for (i = 0; i < sizeof(ivals) / sizeof(int); i++) printf("%d\n", ivals[i]); }
#include <stdlib.h> struct s_ilist { int i; struct s_ilist *next; }; /* * Search for the integer i in the linked list p. * Return a pointer to the first element containing the integer * if found; NULL if the integer is not in the list. */ struct s_ilist * isearch(struct s_ilist *p, int i) { for (; p != NULL; p = p->next) if (p->i == i) return (p); return (NULL); }
#include <stdlib.h> void *bsearch(const void *key, const void *base, size_t num, size_t width, int (*compare)(const void *elem1, const void *elem2));
Hotel Echo Lima Lima Oscar Whiskey Oscar Romeo Lima Delta
/* * Read a line from the standard input and print it according to the * international phonetic alphabet. * * Diomidis Spinellis. March 1999. * * $Id$ * */ #include <stdlib.h> #include <stdio.h> /* * The international phonetic alphabet (sorted) */ char *names[] = { "Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "X-Ray", "Yankee", "Zulu", }; /* * Bsearch compare function */ static int namecmp(char **a, char **b) { return (**a - **b); } main() { char buff[80], *p, **name; fgets(buff, sizeof(buff), stdin); for (p = buff; *p; p++) if (name = (char **)bsearch(&p, names, 26, sizeof(char *), namecmp)) printf("%s\n", *name, *name); }
Παράδειγμα:
Γιάννης 031343454 Φανή 027354566 Θανάσης 014645678 Αλέξανδρος 56789 Γεωργία 034556789 ΤΕΛΟΣ Σωτήρης ΑΓΝΩΣΤΟΣ Φανή 027354566
if (strcmp(buff, "END\n") == 0) ...
Ο παρακάτω πίκανας απεικονίζει τους αντίστοιχους αριθμούς ν στην αρχιτεκτονική Intel Pentium:
Τύπος | ν (bits) |
unsigned char | 8 |
unsigned short | 16 |
unsigned int | 32 |
unsigned long | 32 |
Έτσι για παράδειγμα σε πρόσθεση δύο τιμών τύπου unsigned char 250 + 10 το αποτέλεσμα θα είναι 4 μια και 260 mod 2 8 = 4. Η ιδιότητα αυτή σε συνδυασμό με τις πράξεις πάνω σε bits που ορίζει η C μας επιτρέπει να ορίζουμε εύκολα συναρτήσεις κατακερματισμού.
Οι τελεστές για πράξεις πάνω σε bits ακεραίων αριθμών είναι οι παρακάτω:
Τελεστής | Πράξη |
| | σύζευξη (or) |
& | διάζευξη (and) |
^ | αποκλειστική διάζευξη (exclusive or) |
~ | άρνηση (negation) |
unsigned int hash(unsigned char string) { char *s; unsigned char sum = 0; for (*s = string; *s; s++) sum ^= *s; return (sum & 127); }
Παράδειγμα:
454 3466 456 23 199 Give number: 123 Not known Give number: 456 Known
Η συνάρτηση επιστρέφει μια τιμή τύπου FILE * την οποία το πρόγραμμα χρησιμοποιεί για πρόσβαση στο αρχείο. Αν η πρόσβαση στο αρχείο δεν είναι δυνατή (π.χ. το αρχείο στο οποίο ζητήθηκε ανάγνωση δεν υπάρχει, ένας κατάλογος από το μονοπάτι δεν υπάρχει, το αρχείο το οποίο ζητήθηκε εγγραφή δεν το επιτρέπει) τότε η συνάρτηση fopen επιστρέφει την τιμή NULL.
Ο τύπος πρόσβασης μπορεί να είναι ένας από τους παρακάτω:
Τύπος πρόσβασης | Πρόσβαση |
"r" | Ανάγνωση (read) |
"w" | Εγγραφή (write) |
"a" | Προσθήκη (append) |
"r+w" | Ανάγνωση και εγγραφή |
Στο τέλος της επεξεργασίας του αρχείου πρέπει να καλέσουμε τη συνάρτηση fclose(f) (όπου f η τιμή που επέστρεψε η fopen) για να δηλώσουμε στη βιβλιοθήκη και το λειτουργικό σύστημα να απελευθερώσουν τους πόρους που είχαν δεσμευτεί για την επεξεργασία του αρχείου.
Το παρακάτω πρόγραμμα γράφει στο αρχείο codes.txt τους χαρακτήρες ASCII που διαβάζονται και τους αντίστοιχους κωδικούς τους:
#include <stdio.h> main() { FILE *f; int i; f = fopen("codes.txt", "w"); for (i = ' '; i < '~'; i++) fprintf(f, "Character: %c code:%d\n", i, i); fclose(f); }
Σημείωση: οι συναρτήσεις getc και putc είναι γρήγορες εκδόσεις των gfetc και fputc.
int tbl[100]; fwrite(tbl, sizeof(int), 100, f);
fseek(f, 10 * sizeof(int), SEEK_SET);
C>addphone Name:EKAB Phone:166 C>getphone Name:EKAB Phone=166
#include <iostream.h> main() { cout << "hello, world\n"; }
Σε εγκαταστάσεις Linux το πρόγραμμα πρέπει να έχει επίθεμα .C. Για να μεταγλωττιστεί χρησιμοποιούμε την εντολή g++.
#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;
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"; }
struct point { int x, y; }; point *p; main() { p = new point; delete p; }
main() { int *ip = new int[10]; ip[3] = 8; delete[] ip; }
class point { int x, y; };
Παράδειγμα:
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; }
#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(); }
void point::setpos(int sx, int sy) { this->x = sx; point::y = sy; }
class stack { private: int *values; int size; int sp; public: stack(int size); // Constructor ~stack(); // Destructor }; stack::stack(int size) { stack::size = size; values = new int[size]; } stack::~stack() { delete[] values; }
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); }
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 }
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); }Ο προσδιορισμός αυτός αναγκάζει το μεταγλωττιστή να ελέγχει αν η δέσμευση αυτή τηρείται μέσα στο σώμα της συνάρτησης.
#include <iostream.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 void display(point& p); // Display friend function }; 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"; } void point::setpos(int sx, int sy) { this->x = sx; point::y = sy; } int point::points_used() { return (numpoints); } 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 << "points used = " << point::points_used() << "\n"; delete(c); cout << "points used = " << point::points_used() << "\n"; }
#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(); }
int_queue(int size);
void put_int_queue(int i);
int get_int_queue();
int isempty_int_queue();
I 1 I 2 I 3 O 1 I 4 O 2 ...Παρατήρηση: Το πρόγραμμα μπορεί να δομηθεί γύρω από τον παρακάτω κώδικα εισόδου:
#include <iostream.h> main() { char c; // Initializations here for (;;) { cin >> c; switch (c) { case 'I': // Process input here break; case 'O': // Process output here break; } } }
class υποκλάση : public βασική_κλάση { ...
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); }
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 }
class υποκλάση : public βασική_κλάση1, public βασική_κλάση2, ... { ...Στην περίπτωση αυτή η κλάση κληρονομεί τα μέλη και τις μεθόδους όλων των βασικών κλάσεων που δηλώθηκαν.
#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(); }
= 0τότε η συνάρτηση αυτή είναι θεωρητική (pure) δηλαδή δεν έχει υλοποίηση στη συγκεκριμένη κλάση. Παράδειγμα:
class document { public: virtual char *Identify() = 0; };
Για παράδειγμα, ένας σχεδιασμός του πληροφοριακού συστήματος του Πανεπιστημίου μπορεί να ορίσει την αφηρημένη κλάση person ως βασική κλάση για τις υποκλάσεις student, employee και visitor. Αν και δε θα μπορεί να οριστεί μια μεταβλητή για την αφηρημένη κλάση person, αυτή μπορεί να περιέχει ορισμένα βασικά χαρακτηριστικά όπως birth_date και να επιβάλει την υλοποίηση συγκεκριμένων μεθόδων όπως home_page_URL() ορίζοντάς τις ως θεωρητικές.
#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"; }Χρησιμοποιούμε πρότυπα:
Ο ορισμός του προτύπου περιέχει μετά τη δεσμευμένη λέξη 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"; }
#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"; }
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
#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); }
using namespace std;Στις επόμενες ενότητες περιγράφουμε συνοπτικά μόνο τις γενικές λειτουργίες της βιβλιοθήκης και με περισσότερες λεπτομέρειες τις λειτουργίες της STL.
Στην STL ορίζονται οι παρακάτω πρότυποι (ως προς τα στοιχεία που περιέχουν) περιέχοντες:
#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"; }
I 1 I 2 I 3 O 1 I 4 O 2 ...
Μερικές φορές το σύστημα βοηθείας συμπληρώνεται από
οδηγούς (wizards) που επιτρέπουν με διαλογικό
τρόπο τη βήμα με βήμα ανάπτυξη μιας εφαρμογής.
Τα παρακάτω σχήμα παριστάνει ένα στάδιο από την εκτέλεση ενός
οδηγού:
Συχνά υπάρχει άμεση σύνδεση του διορθωτή με το σύστημα βοήθειας έτσι ώστε την ώρα που π.χ. πληκτρολογούμε την κλήση σε μια συνάρτηση της βιβλιοθήκης να μπορούμε να δούμε τον ορισμό της.
Σχεδιάζουμε για να μπορέσουμε να καταλάβουμε το σύστημα που αναπτύσσουμε. Έτσι δημιουργώντας ένα σχέδια επιτυγχάνουμε τέσσερις στόχους:
Σε όλους τους τεχνολογικούς τομείς ο σχεδιασμός βασίζεται σε τέσερις βασικές αρχές:
Η UML περιλαμβάνει τρία βασικά στοιχεία:
Αν σε μια σχέση τα αντικείμενα απαρτίζουν τμήματα ενός όλου, τότε
αυτή απεικονίζεται ως συγκρότημα (aggregation)
με την παράσταση ενός διαμαντιού στην άκρη του "όλου".
Αν σχέση τα αντικείμενα που απαρτίζουν τμήματα ενός όλου έχουν
την ίδια διάρκεια ζωής με το όλο, τότε
αυτή απεικονίζεται ως σύνθεση (composition)
με την παράσταση ενός γεμάτου διαμαντιού στην άκρη του "όλου".
Το σύνολο των αντικειμένων σχεδιάζεται με βάση τους συνδέσμους που
ορίζονται πάνω σε αυτό.
Το σχεσιακό μοντέλο (relational model) δεδομένων παριστάνει δεδομένα και τις σχέσεις τους ως ένα σύνολο πινάκων. Κάθε πίνακας (table) αποτελείται από στήλες (columns) με μοναδικά ονόματα. Μια γραμμή (row) του πίνακα παριστάνει μια σχέση (relationship) ανάμεσα σε ένα σύνολο από τιμές. Ο πίνακας που ακολουθεί παριστάνει έναν τηλεφωνικό κατάλογο. Αποτελείται από δύο στήλες και πέντε γραμμές.
Όνομα | Τηλέφωνο |
Γιώργος | 32560 |
Μαρία | 61359 |
Θανάσης | 98756 |
Λίνα | 78999 |
Πέτρος | 12356 |
Η SQL (structured query language) αποτελεί σήμερα την πιο διαδεδομένη γλώσσα διαχείρισης σχεσιακών βάσεων δεδομένων. Η SQL παρέχει δυνατότητες για:
Η SQL είναι ορισμένη ως διεθνές πρότυπο. Στις επόμενες ενότητες θα εξετάσουμε ένα υποσύνολο της SQL όπως υποστηρίζεται από την εγκατεστημένη στα εργαστήρια βάση δεδομένων Microsoft Access.
Στην περιγραφή της σύνταξης της SQL θα χρησιμοποιήσουμε τα παρακάτω σύμβολα:
CREATE TABLE όνομα_πίνακα (πεδίο1 τύπος [(μέγεθος)] [, πεδίο2 τύπος [(μέγεθος)] [, ...]]Για παράδειγμα η εντολή
CREATE TABLE Customer (Name TEXT (20), AccountNum SHORT)δημιουργεί τον πίνακα με όνομα Customer και με δύο στήλες: την Name και την AccountNum.
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 [ UNIQUE ] INDEX όνομα_δείκτη ON όνομα_πίνακα (πεδίο [ASC|DESC][, πεδίο [ASC|DESC], ...])Η λέξη UNIQUE ορίζει πως δε θα επιτρέπονται διπλές εμφανίσεις μιας τιμής για το συγκεκριμένο δείκτη. Οι λέξεις ASC και DESC ορίζουν αύξουσα ή φθίνουσα ταξινόμηση του πίνακα με βάση το δείκτη. Παράδειγμα:
CREATE INDEX NameIdx ON Customer (Name)Δημιουργεί το δείκτη NameIdx για τη στήλη Name στον πίνακα Customer.
Ένας δείκτης μπορεί να διαγραφεί με τη σύνταξη:
DROP INDEX όνομα_δείκτη ON όνομα_πίνακα
INSERT INTO όνομα_πίνακα [(πεδίο1[, πεδίο2[, ...]])] VALUES (τιμή1[, τιμή2[, ...])Παράδειγμα:
INSERT INTO Customer (Name, AccountNum) VALUES ("Papadopoulos", 1234)
SELECT πεδία FROM πίνακες [ WHERE κριτήρια ]Απλό παράδειγμα:
SELECT Name, AccountNum FROM Customer WHERE Name LIKE "Pap*"
Τα πεδία μπορούν να είναι ονόματα πεδίων ή συγκεντρωτικές συναρτήσεις της SQL πάνω σε πεδία. Τέτοιες συναρτήσεις είναι οι παρακάτω:
SELECT Sum(AccountBalance) FROM CustomerΟ αστερίσκος ως ορισμός πεδίου επιλέγει όλα τα πεδία.
Τα κριτήρια αναζήτησης είναι εκφράσεις πάνω στα πεδία. Ορισμένοι βασικοί τελεστές είναι οι:
SELECT * FROM Customer WHERE Balance > 10000 AND Name LIKE "Papad*"
Βασικό στοιχείο του σχεσιακού μοντέλου είναι η αποθήκευση δεδομένων σε πίνακες που σχετίζονται μεταξύ τους. Έτσι για παράδειγμα ένας πίνακας μπορεί να φυλάει τα στοιχεία των πελατών και ένας άλλος τους λογαριασμούς. Με τον τρόπο αυτό τα στοιχεία ενός πελάτη που έχει πολλούς λογαριασμούς σε μια τράπεζα φυλάσσονται μόνο μια φορά. Ανάκτηση από δύο τέτοιους πίνακες γίνεται με τον προσδιορισμό τους στο SELECT μαζί με τη χρήση της κατάλληλης έκφρασης που θα ενώσει τους πίνακες. Παράδειγμα:
SELECT Customer, Balance FROM Customers, Accounts WHERE Customer.AccountId = Accounts.AccountId
UPDATE όνομα_πίνακα SET πεδίο = νέα_τιμή WHERE κριτήρια;Παράδειγμα:
UPDATE Accounts SET Balance=100000 WHERE AccountId = 12233
DELETE FROM όνομα_πίνακα WHERE κριτήριαΠαράδειγμα:
DELETE FROM Accounts WHERE AccountId = 123232
Βασικά χαρακτηριστικά του συναρτησιακού προγραμματισμού είναι:
Ορισμένες γνωστές συναρτησιακές γλώσσες προγραμματισμού είναι οι:
Οι βασικές ιδέες της γλώσσας Lisp αναπτύχθηκαν από τον John McCarthy το 1956 σε ένα ερευνητικό πρόγραμμα για τεχνητή νοημοσύνη. Στόχος του McCarthy ήταν η ανάπτυξη μιας αλγευρικής γλώσσας επεξεργασίας λιστών για έρευνα στην τεχνητή νοημοσύνη. Ανάμεσα στα έτη 1960 και 1965 υλοποιήθηκε η διάλεκτος Lisp 1.5 η οποία τη δεκαετία του 1970 είχε οδηγήσει στις διαλέκτους MacLisp και InterLisp. Στα μέσα της δεκαετίας του 1970 οι Sussman και Steele Jr. ανέπτυξαν τη διάλεκτο Scheme με βάση ιδέες από τη σημασιολογία των γλωσσών προγραμματισμού. Στη δεκαετία του 1980 έγινε προσπάθεια για ενοποίηση των διαλέκτων της Lisp κάτω από το πρότυπο της Common Lisp.
(defun όνομα_συνάρτησης (παράμετροι) τιμή)
(όνομα_συνάρτησης παράμετροι)
(defun factorial (n) (if (equal n 0) 1 (* n (factorial (- n 1)))))
'(1 2 3 4 42) 'word '('Maria 'John 'Aliki)Βασικές συναρτήσεις επεξεργασίας λιστών είναι οι παρακάτω:
(defun mylength (a) (if (null a) 0 (+ (mylength (cdr a)) 1)))
(defun myappend (a b) (if (null a) b (cons (car a) (myappend (cdr a) b))))
(defun myreverse (a) (if (null a) nil (myappend (myreverse (cdr a)) (cons (car a) nil))))
(lambda όνομα_συνάρτησης (όρισμα) τιμή)
(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)
(defun myreduce (f v lst) (if (null lst) v (apply f (cons (car lst) (cons (myreduce f v (cdr lst)) nil)))))Έτσι μπορούμε να ορίσουμε συναρτήσεις όπως τις:
(defun mysum (lst) (myreduce '+ 0 lst))
(defun myproduct (lst) (myreduce '* 1 lst))
(defun alltrue (lst) (myreduce 'and t lst))
(defun anytrue (lst) (myreduce 'or nil lst))
Πριν δύο δεκαετίες, η φύση των υπολογιστικών συστημάτων εκφράζονταν με την ύπαρξη ισχυρών κεντρικών υπολογιστικών συστημάτων (mainframes) στα οποία ήταν εγκατεστημένες εφαρμογές οι οποίες μπορούσαν να προσπελαστούν από τους χρήστες μέσα από τα τερματικά του συστήματος.
Με την εμφάνιση και την ανάπτυξη των προσωπικών υπολογιστών και καθώς οι εφαρμογές άρχισαν να γίνονται ολοένα και πιο μεγάλες και σύνθετες, άρχισαν να μεταμορφώνονται από ενιαία προγράμματα σε κομμάτια ικανά να συνεργάζονται μεταξύ τους. Στην διάσπαση των εφαρμογών σε περισσότερα από ένα μέρη, συνέβαλλε η ανάπτυξη της αρχιτεκτονικής πελάτη/εξυπηρετητή (client/server) βάσει της οποίας γινόταν η υλοποίησή τους. Κατά την αρχιτεκτονική πελάτη/εξυπηρετητή, μία διεργασία καλείται πελάτης (client process) όταν αιτείται την υλοποίηση κάποιων υπηρεσιών-μεθόδων από μία διεργασία η οποία είναι ικανή να της προσφέρει τις επιθυμητές υπηρεσίες. Η τελευταία αυτή διεργασία καλείται διεργασία του εξυπηρετητή (server process).
Αργότερα με την ανάπτυξη των υπολογιστικών δικτύων τα συνθετικά μέρη των εφαρμογών, προκειμένου να υπάρξει καλύτερη και αποτελεσματικότερη εκμετάλλευση των νέων δυνατοτήτων, άρχισαν να διαμοιράζονται στους υπολογιστές του δικτύου. Με τον τρόπο αυτό αυξανόταν η απόδοση και εκμετάλλευση των πόρων του δικτύου. Αυτού του είδους οι εφαρμογές ονομάζονται κατανεμημένες εφαρμογές (distributed applications).
Η δυνατότητα διασύνδεσης, σε δίκτυα, υπολογιστικών συστημάτων διαφορετικής αρχιτεκτονικής είχε σαν αποτέλεσμα την δημιουργία ενός ανομοιογενούς υπολογιστικού περιβάλλοντος. Θα έπρεπε, λοιπόν, και οι εφαρμογές να μπορέσουν να εξελιχθούν έτσι ώστε να είναι δυνατή η λειτουργία τους σε τέτοια ετερογενή συστήματα υπολογιστών. Αυτή η εξέλιξη οδήγησε στην εμφάνιση των ετερογενών κατανεμημένων εφαρμογών (heterogeneous distributed applications).
Τρία είναι τα βασικά μοντέλα των οποίων σκοπός είναι η υλοποίηση των παραπάνω και αυτά είναι:
Σύμφωνα με την αρχιτεκτονική που προτείνεται από την 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.
Και εδώ, ο βασικός σκοπός του μοντέλου είναι να καταστήσει δυνατή την επικοινωνία δύο ή περισσοτέρων εφαρμογών ή συνθετικών, ακόμα και στην περίπτωση που αυτά διαφέρουν ως προς την προέλευση, τη γλώσσα προγραμματισμού, τα μηχανήματα στα οποία βρίσκονται και τα λειτουργικά συστήματα κάτω από τα οποία τρέχουν.
Ο "μηχανισμός λειτουργίας του μοντέλου είναι παρόμοιος με αυτόν της CORBA. Βασικό ρόλο παίζουν οι διασυνδέτες τα οποία δεν είναι τίποτα άλλο από ένα σαφές διατυπωμένο "συμβόλαιο" μεταξύ των "κομματιών" λογισμικού, το οποίο περιέχει ένα σύνολο από σχετικές λειτουργίες. Όταν λέμε ότι ένα αντικείμενο υλοποιεί έναν διασυνδέτη αυτό σημαίνει ότι το αντικείμενο αυτό υλοποιεί κάθε λειτουργία του.
Πως γίνεται, όμως, η διαδικασία της αιτήσεως κάποιων λειτουργιών από έναν πελάτη;
Για να μπορέσει ένας πελάτης να εξυπηρετηθεί από κάποιο αντικείμενο, θα πρέπει η γλώσσα προγραμματισμού, στην οποία είναι υλοποιημένος, να έχει τη δυνατότητα δημιουργίας δεικτών και να καλεί συναρτήσεις μέσω των δεικτών. Θα πρέπει λοιπόν ο πελάτης να έχει τον κατάλληλο δείκτη προς τον επιθυμητό διασυνδέτη τον οποίο περιέχει τις επιθυμητές από τον πελάτη λειτουργίες.
Στην περίπτωση όμως που ο πελάτης δεν έχει τον κατάλληλο δείκτη προς το επιθυμητό διασυνδέτη, τότε απευθύνεται προς την COM δίνοντας σαν στοιχεία την ταυτότητα της κλάσης του εξυπηρετητή που επιθυμεί ο πελάτης και τον τύπο του, δηλαδή εάν είναι:
Η COM τότε αναλαμβάνει να βρει τον επιθυμητό εξυπηρετητή και να επιστρέψει στον πελάτη τον επιθυμητό δείκτη.Όπως έγινε σαφές, ο κάθε πελάτης μπορεί να είναι υλοποιημένος σε οποιαδήποτε γλώσσα προγραμματισμού, αρκεί αυτή να υποστηρίζει την κατασκευή και την χρήση δεικτών. Για τα "interfaces", όμως, υπάρχει και εδώ μία "IDL" (interface Definition Language) η οποία επιτρέπει και βοηθάει τους σχεδιαστές να κατασκευάζουν διασυνδέτες.
Η τεχνολογία RMI αφορά αποκλειστικά αντικείμενα τα οποία είναι κατασκευασμένα με τη γλώσσα προγραμματισμού Java. Αυτό έχει ως αποτέλεσμα να δίνει την δυνατότητα της πλήρους εκμετάλλευσης των δυνατοτήτων της Java αλλά και το πλεονέκτημα του ομοιογενούς, ως προς τη γλώσσα προγραμματισμού, περιβάλλοντος.
Η αρχιτεκτονική ενός RMI συστήματος ακολουθεί την δομή των στρωμάτων-επιπέδων (layers). Υπάρχουν τρία (συν ένα) επίπεδα:
Η διαδικασία επίκλησης κάποιων υπηρεσιών από έναν πελάτη, δεν διαφέρει, ιδιαίτερα, σε σχέση με τις προαναφερθείσες τεχνολογίες. Ένας πελάτης, ο οποίος επιθυμεί την υλοποίηση κάποιων υπηρεσιών, υποβάλλει μία αίτηση προς το RMI-σύστημα. Στην αίτηση αυτή θα πρέπει να αναφέρονται οι κατάλληλες εκείνες πληροφορίες οι οποίες απαιτούνται για την αποστολή της αίτησης προς τον κατάλληλο εξυπηρετητή. Αυτές οι πληροφορίες-παράμετροι είναι: μία αναφορά του επιθυμητού αντικειμένου (object reference), οι επιθυμητές μέθοδοι και οι κατάλληλες παράμετροι για την υλοποίηση των μεθόδων.
Από τη στιγμή που ο πελάτης καταθέσει την αίτησή του, το RMI-σύστημα είναι υπεύθυνο για την ανεύρεση του κατάλληλου εξυπηρετητή, την μεταβίβαση της αιτήσεων και την επιστροφή των αποτελεσμάτων στον πελάτη.
Όμοια με την CORBA και την COM/DCOM, σε ένα RMI-σύστημα, οι πελάτες δεν έρχονται ποτέ σε απευθείας επικοινωνία με τα επιθυμητά αντικείμενα παρά μόνο μέσω των διασυνδετών αυτών των αντικειμένων. Και εδώ ο διασυνδέτης είναι αυτός που περιέχει τις μεθόδους-υπηρεσίες τις οποίες μπορεί να υλοποιήσει το αντικείμενο.
Η επικοινωνία πελάτη-αντικειμένου, σΤ ένα σύστημα RMI, είναι η ίδια ανεξάρτητα με το που βρίσκεται το επιθυμητό αντικείμενο (αν δηλαδή βρίσκεται στην ίδια διεργασία με τον πελάτη, αν βρίσκεται τοπικά ή απομακρυσμένα).
Η εξέλιξη της ισχύος των υπολογιστών και του αντίστοιχου θεωρητικού υπόβαθρου μας έχει επιτρέψει να εξετάζουμε διαφορετικές προσεγγίσεις επίλυσης προβλημάτων με υπολογιστή που να βασίζονται όχι στο να κάνουν την αρχιτεκτονική του υπολογιστή προσιτή στον άνθρωπο, αλλά στο να κάνουν τη διατύπωση των προβλημάτων από τον άνθρωπο προσιτή στον υπολογιστή. Έτσι, τόσο ο συναρτησιακός όσο και ο λογικός προγραμματισμός επιτρέπουν τη διατύπωση του προβλήματος που πρέπει να επιλυθεί με βάση έναν συμβολισμό και αφήνουν στον υπολογιστή να λύσει το πρόβλημα με βάση τη διατύπωσή του. Η βάση του λογικού προγραμματισμού μπορεί να διατυπωθεί ως εξής:
Παράδειγμα 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
?- 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, τον όρο. Οι όροι ορίζονται αναδρομικά. Ένας όρος μπορεί να είναι:
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) :- father(Z, X), father(Y, Z). ?- grandfather(X, Y). X = giannis Y = nikos ->; X = giannis Y = giorgos ->; no
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
?- 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
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
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
polynomial_value(P, 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.
υπολογισμός | μετατροπή σε λέξη | φωνητική ανάγνωση 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; }
Κάθε λύση στο πρόβλημα του αμοιβαίου αποκλεισμού (mutual exclusion) μεταξύ των κρίσιμων τμημάτων πρέπει να εξασφαλίζει:
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.
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); } }
Η δομή αυτή εξασφαλίζει τον αμοιβαίο αποκλεισμό κρισίμων τμημάτων. H αναστολή διεργασιών που δεν μπορούν να συνεχίσουν εξασφαλίζεται με τη χρήση των μεταβλητών συνθήκης (condition variables)
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); } }
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.
HANDLE mutex; /* Declare semaphore variable */ mutex = CreateMutex(NULL, FALSE, NULL); /* Create semaphore */
WaitForSingleObject(mutex, INFINITE);
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);
cl -D_MT /MT test.c
/* * 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); }
Τμήμα
Πληροφοριακών
και Επικοινωνιακών
Συστημάτων
Γλώσσες προγραμματισμού και δομές δεδομένων
Διδάσκων: Διομήδης Σπινέλλης | Εξεταστική περίοδος
Ιουνίου 1999 |
Θέμα 1ο: (3 βαθμοί)
Να ορίσετε σε C μια δομή struct s_list για την παράσταση μιας συνδεδεμένης λίστας ακεραίων με τη χρήση δεικτών. Υλοποιήστε σε C τη συνάρτηση
struct s_list *find_int(int i);
που επιστρέφει
δείκτη στο
πρώτο στοιχείο
της λίστας
που περιέχει
τον ακέραιο
i ή
NULL αν
αυτός δεν υπάρχει
στη λίστα.
Σε μια λίστα
με N στοιχεία
πόσα κατά μέσο
όρο στοιχεία
θα εξεταστούν
για να βρεθεί
ο ακέραιος
i;
Θέμα 2ο: (3 βαθμοί)
Μια εφαρμογή
αναγνώρισης
κλήσεων για
γνωστή αλυσίδα
διανομής φαγητού
πρέπει να μπορεί
γρήγορα να
εμφανίσει
το όνομα που
αντιστοιχεί
σε κάθε έναν
από 5.000.000 αριθμούς
τηλεφώνου.
Περιγράψτε
αδρά (σε λιγότερο
από μια σελίδα)
μια κατάλληλη
τεχνολογία
ή δομή δεδομένων
που να ικανοποιεί
την απαίτηση
αυτή καθώς
και την τεχνική
αναζήτησης
που θα πρέπει
να χρησιμοποιηθεί.
Θέμα 3ο: (3 βαθμοί)
Για την παρακάτω κλάση της C++ που ορίζει μια στοίβα 100 ακεραίων υλοποιήστε τις συναρτήσεις stack, push και pop. Γράψτε σε C++ ένα πρόγραμμα που να διαβάζει 40 ακεραίους και να τους τυπώνει με ανάποδη σειρά (από το τέλος προς την αρχή).
class stack { private: int sp; // Stack pointer int elem[100]; // Stack elements public: stack(void); // Constructor void push(int i); int pop(void); };
Θέμα 4ο: (3 βαθμοί)
Η παρακάτω συνάρτηση της Lisp επιστρέφει το παραγοντικό του αριθμού n:
(defun factorial (n) (if (equal n 0) 1 (* n (factorial (- n 1)))))
Ορίστε με ανάλογο τρόπο συνάρτηση σε Lisp που να υπολογίζει τον νοστό όρο της ακολουθίας Fibonacci δεύτερης τάξης:
F0 = 0, F1 = 1,
Fn + 2 = Fn + 1 + Fn
+ Fn
Το άριστα ορίζεται ως 10 βαθμοί.
Διάρκεια εξέτασης 2,5 ώρες | Καλή επιτυχία! |
Τμήμα Πληροφοριακών και Επικοινωνιακών Συστημάτων
Γλώσσες προγραμματισμού και δομές δεδομένων
Διδάσκων: Διομήδης Σπινέλλης | Εξεταστική περίοδος
Σεπτεμβρίου 1999 |
Θέμα 1ο: (3 βαθμοί)
Να ορίσετε σε C μια δομή struct s_btree για την παράσταση ενός δυαδικού δένδρου αναζήτησης χαρακτήρων. Υλοποιήστε σε C τη συνάρτηση
struct s_btree *find_char(struct s_btree *root, char c);που επιστρέφει δείκτη στο στοιχείο του δένδρου που περιέχει τον ακέραιο c ή NULL αν αυτός δεν υπάρχει στο δένδρο.
Θέμα 2ο: (3 βαθμοί)
Ένα ταξιδιωτικό γραφείο σας ζητάει ένα πρόγραμμα που να μπορεί να απαντά σε ερωτήσεις του τύπου "ποιες πτήσεις πρέπει να ακολουθήσω για να πάω από τη Σάμο στο Κατμαντού;" (Σάμος - Αθήνα - Άμστερνταμ - Πεκίνο - Κατμαντού). Σε ποια δομή δεδομένων θα πρέπει να βασιστεί ένα τέτοιο πρόγραμμα; Περιγράψτε (με ένα μικρό παράδειγμα σε C) έναν τρόπο υλοποίησης της δομής αυτής.
Θέμα 3ο: (3 βαθμοί)
Για την παρακάτω κλάση της C++ που ορίζει μια ουρά 100 ακεραίων υλοποιήστε τις συναρτήσεις queue, add και remove. Γράψτε σε C++ ένα πρόγραμμα που να διαβάζει ακεραίους και, αφού διαβάσει τους πρώτους 20, να τους τυπώνει με τη σειρά ανάγνωσης: έναν αριθμό για κάθε νέο ακέραιο που θα διαβάζει. (Όταν δηλαδή διαβάσει τον 21ο θα τυπώσει τον 1ο, μετά τον 22ο θα τυπώσει τον 2ο, κ.ο.κ.)
class queue { private: int head; // Pointer to the queue head int tail; // Pointer to the queue tail int elem[100]; // Queue elements public: queue(void); // Constructor void add(int i); // Adds an integer to the queue tail int remove(void); // Removes and returns an integer from // the queue head };
Θέμα 4ο: (3 βαθμοί)
Τα παρακάτω κατηγορήματα της Prolog ορίζουν τους ακέραιους αριθμούς (nat) και την πρόσθεση με βάση τον όρο suc που ορίζει τον επόμενο ενός ακεραίου:
nat(0). nat(suc(X)) :- nat(X). plus(0, X, X) :- nat(X). plus(suc(X), Y, suc(Z)) :- plus(X, Y, Z).Να ορίσετε, βασισμένοι στα παραπάνω, το κατηγόρημα double(X, Y) που είναι αληθές όταν και μόνο όταν το Υ είναι το διπλάσιο του Χ.
Το άριστα ορίζεται ως 10 βαθμοί.
Διάρκεια εξέτασης 2,5 ώρες | Καλή επιτυχία! |
Α.Μ. Βαθμός ----- ------ 98001 5 98002 0 98003 0 98004 7 98005 0 98006 2 98007 7 98008 0 98009 6 98010 0 98011 6 98012 3 98013 0 98014 0 98015 1 98016 0 98017 6 98018 0 98019 4 98020 5 98021 0 98022 7 98023 0 98024 0 98025 0 98026 0 98027 7 98028 4 98029 7 98030 1 98031 0 98032 0 98033 0 98034 0 98035 1 98036 1 98037 7 98038 0 98039 1 98040 0 98041 0 98042 5 98043 0 98044 1 98045 6 98046 6 98047 7 98048 7 98049 6 98050 2 98051 0