/* ============================================================================
* Από: migf1
*
* Γράψτε ένα πρόγραμμα που θα διαβάζει από το πληκτρολόγιο τυχαίο πλήθος λέξεων (μια
* λέξη ανά γραμμή) και σε τυχαία σειρά μέχρι να διαβάσει τη λέξη stop. Για ευκολία
* θεωρήστε δεδομένο πως οι λέξεις αποτελούνται από λατινικούς χαρακτήρες και πως η
* κάθε γραμμή περιέχει μια μόνο λέξη.
*
* Κάθε λέξη (συμπεριλαμβανομένης της stop) θα μετατρέπεται πρώτα σε πεζά γράμματα και
* κατόπιν ανάλογα με το γράμμα από το οποίο αρχίζει θα καταχωρείται στην λίστα που
* αντιστοιχεί στο γράμμα αυτό, σε αντίστροφη χρονολογική σειρά (δηλαδή οι πιο πρόσφατα
* πληκτρολογημένες λέξεις θα εμφανίζονται πρώτες στη λίστα τους).
*
* Δηλαδή όσες λέξεις ξεκινάνε με το λατινικό γράμμα a θα καταχωρούνται στη λίστα του
* a, όσες ξεκινάνε από b θα καταχωρούνται στη λίστα του b, και ούτω καθ' εξής. Αν μια
* λέξη αρχίζει από χαρακτήρα με ASCII code μικρότερο του 'a' θα μπαίνει στη λίστα του
* a, ενώ αν αρχίζει από χαρακτήρα με ASCII code μεγαλύτερο του 'z' θα μπαίνει στη
* λίστα του z.
*
* Πριν τερματίσει το πρόγραμμα θα τυπώνει τα περιεχόμενα (λέξεις) μόνο όσων λιστών δεν
* είναι άδειες, καθώς και το σε ποιο γράμμα αντιστοιχούν και πόσες λέξεις περιέχουν.
* Στο τέλος θα τυπώνει το συνολικό πλήθος των λέξεων που περιέχονται σε όλες τις λίστες.
*
* Περισσότερα: http://x...content-available-to-author-only...s.gr/prg/c-notes/linked-lists/intro/
* ============================================================================
*/
#include <stdio.h>
#include <stdlib.h> // για calloc(), free(), exit()
#include <string.h> // για strncmp()
#include <ctype.h> // για tolower()
#define MAXINBUF 255+1 // μέγιστο μήκος γραμμής εισόδου + '\0'
#define listempty(head) ( (head) == NULL ) // συνθήκη άδειας λίστας
typedef enum { FALSE=0, TRUE } Bool; // ο δικός μας τύπος boolean
typedef struct node { // δομή για κάθε κόμβο της λίστας
char data[ MAXINBUF ]; // c-string μιας λέξης
struct node *next; // δείκτης στον επόμενο κόμβο
} Node;
typedef struct { // η δομή της λίστας
Node *head, *tail; // δεικτες στην αρχή και στο τέλος
int len; // μήκος λίστας
} List;
// -----------------------------------------------------------------------------------
// ΣΥΝΑΡΤΗΣΗ: s_getflushed
// βελτιωμένη παραλλαγή της fgets για την κύρια είσοδο (σβήνει το '\n' + fflush)
//
// Διαβάζει από την κύρια είσοδο έως len-1 χαρακτήρες ή μέχρι να πατηθεί ENTER,
// τους καταχωρεί στο s και το επιστρέφει με μηδενισμένο τον τελικό χαρακτήρα.
// Αν πληκτρολογήθηκαν len-1 χαρακτήρες πριν πατηθεί το ENTER, τότε οι περιττοί
// χαρακτήρες αφαιρούνται από το buffer της εισόδου (stdin).
//
char *s_getflushed(char *s, size_t len)
{
char *cp;
for (cp
=s
; (*cp
=getc(stdin
)) != '\n' && (cp
-s
) < len
-1; cp
++ ) ;
if ( *cp != '\n') { // len reached withoutn '\n'
*cp = '\0'; // null terminate s and
while (getc(stdin
) != '\n') // flush remaining chars ;
}
else // '\n' found
*cp = '\0'; // null-terminate
return s;
}
// ------------------------------------------------------------------------------
// ΣΥΝΑΡΤΗΣΗ: s_tolower
// μετατρέπει όλους τους χαρακτήρες του c-string s σε πεζούς και το επιστρέφει
//
char *s_tolower(char *s)
{
char *ret;
for ( ret
=s
; (*s
=tolower(*s
)); s
++ ); return ret;
}
// ------------------------------------------------------------------------------
// ΣΥΝΑΡΤΗΣΗ: s_ncopy
// παραλλαγή της strncpy με εγγυημένο μηδενισμό του τελικού χαρακτήρα
// (αντιγράφει έως n-1 χαρακτήρες από το c-string dst στο src, και το επιστρέφει)
//
char *s_ncopy( char *dst, const char *src, size_t n )
{
char *save = dst;
while ( (dst-save) < n-1 && (*dst=*src) != '\0' )
dst++, src++;
if ( *dst )
*dst = 0;
return save;
}
// ------------------------------------------------------------------------------
// ΣΥΝΑΡΤΗΣΗ: list_prepend
// δημιουργεί νέο κόμβο για τη λέξη data, τον εισαγάγει στην αρχή της list και
// ενημερώνει το μήκος της (επιστρέφει FALSE σε περίπτωση σφάλματος, αλλιώς TRUE)
//
Bool list_prepend( List *list, char *data )
{
Node
*new
= calloc(1, sizeof(Node
) ); // δημιουργία νέου κόμβου if ( !new ) // αποτυχία δέσμευσης μνήμης
return FALSE; // επιστροφή σφαλματος
s_ncopy(new->data, data, MAXINBUF); // αρχικοποίηση νέου κόμβου
// εισαγωγή νέου κόμβου στην αρχή της λίστας
if ( list->head == NULL ) { // σε κενή λίστα
new->next = NULL; // ...
list->head = list->tail = new; // ...
}
else { // σε μη κενή λίστα
new->next = list->head; // ...
list->head = new; // ...
}
(list->len)++; // ενημέρωση μήκους λίστας
return TRUE; // επιστροφή επιτυχίας
}
// ------------------------------------------------------------------------------
// ΣΥΝΑΡΤΗΣΗ: list_print
// τυπώνει τα περιεχόμενα της list
//
void list_print( List list )
{
while ( list.head ) {
printf("%s ", list.
head->data
); list.head = list.head->next;
}
return;
}
// ------------------------------------------------------------------------------
// ΣΥΝΑΡΤΗΣΗ: list_destroy
// αποδεσμεύει όση μνήμη είχε δεσμευτεί για την list
//
void list_destroy( List *list )
{
if ( list->head == NULL )
return;
Node *dummy = NULL;
while ( list->head ) {
dummy = list->head->next;
list->head = dummy;
}
return;
}
// ------------------------------------------------------------------------------
// ΣΥΝΑΡΤΗΣΗ: hashtab_init
// αρχικοποιεί τον διαχωριστικό πίνακα hashtab με μηδενικές τιμές
//
void hashtab_init( List hashtab[] )
{
register int i;
for (i=0; i < 26; i++) {
hashtab[ i ].head = NULL;
hashtab[ i ].tail = NULL;
hashtab[ i ].len = 0;
}
return;
}
// ------------------------------------------------------------------------------
// ΣΥΝΑΡΤΗΣΗ: hashtab_destroy
// αποδεσμεύει τη μνήμη όλων τις λιστών του διαχωριστικού πίνακα hashtab
//
void hashtab_destroy( List hashtab[] )
{
register int i;
for (i=0; i < 26; i++)
list_destroy( &hashtab[ i ] );
return;
}
// ------------------------------------------------------------------------------
int main( void )
{
List hashtab[ 26 ]; // διαχωριστικός πίνακας 26 λιστών
char inbuf[ MAXINBUF ] = ""; // γραμμή κύριας εισοόδου
int i, total; // total = συνολικό πλήθος λέξεων
hashtab_init( hashtab ); // αρχικοποίηση διαχωριστικού πίνακα
/*
* είσοδος, επεξεργασία & καταχώρηση λέξεων στις λίστες του διαχωριστικού πίνακα
*/
puts("Πληκτρολογήστε μια λέξη ανά γραμμή ή \"stop\" για τερματισμό (λατινικά γράμματα)\n"); do {
s_getflushed( inbuf, MAXINBUF ); // ανάγνωση γραμμής από stdin
if ( !*inbuf ) // ... κενή γραμμή
continue; // ...
i = (int)( *s_tolower(inbuf) - 'a' ); // μετατροπή σε πεζά γράμματα
if ( i < 0 ) { // αντιμετώπιση κάτω υπερχείλισης
puts("\tοι λέξεις πρέπει να αρχίζουν από λατινικό γράμμα"); puts("\t( η λεξη σας καταχωρήθηκε στη λίστα του 'a' )"); i = 0;
}
else if ( i > 'z' - 'a') { // αντιμετώπιση άνω υπερχείλισης
puts("\tοι λέξεις πρέπει να αρχίζουν από λατινικό γράμμα"); puts("\t( η λεξη σας καταχωρήθηκε στη λίστα του 'z' )"); i = 'z' - 'a';
}
// εισαγωγή στην κατάλληλη λίστα
if ( !list_prepend( &hashtab[ i ], inbuf ) ) {
puts("*** Σφάλμα: Ανεπαρκής μνήμη, τερματισμός προγράμματος..."); hashtab_destroy( hashtab );
}
} while ( !*inbuf
|| strncmp(inbuf
, "stop", MAXINBUF
) );
/*
* τύπωμα των λέξεων και του πλήθους τους ανά λίστα και συνολικά
*/
total = 0;
for (i=0; i < 26; i++)
{
if ( !listempty( hashtab[i].head ) ) { // γεμάτη λίστα
printf("%c (%d λέξεις): ", i
+'a', hashtab
[i
].
len); list_print( hashtab[ i ] );
total += hashtab[i].len;
}
}
printf("\nΔώσατε %d λέξεις συνολικά (συμπεριλαμβανομένης της \"stop\")\n",total
);
/*
* εκκαθάριση και τερματισμός προγράμματος
*/
hashtab_destroy( hashtab ); // αποδέσμευση μνήμης
}