gifour Δημοσ. 20 Σεπτεμβρίου 2016 Δημοσ. 20 Σεπτεμβρίου 2016 Καλησπέρα, στα πλαισια ενός on line course που παρακολουθώ ολοκλήρωσα μια άσκηση που απαιτεί την υλοποίηση τεσσάρων λειτουργιών για μια data structure dictionary τα οποία χρησιμοποιεί ένα προγραμμα spell-check. Οι λειτουργίες είναι οι: - "load" που φορτώνει το φάκελο .txt του λεξικού στη μνήμη, - "check" που ελέγχει κάθε λέξη που της περνάει το πρόγραμμα "speller" για το αν αυτή η λέξη υπάρχει στο λεξικό, - "size" που δίνει τον αριθμό των λέξεων που φορτώθηκαν από το λεξικό και - "unload" που ελευθερώνει την δεσμευμένη μνήμη. το πρόγραμμα τρέχει με ./speller [file: dictionary] <file : script> και τυπώνει στην κονσόλα τις λέξεις του script που δεν υπάρχουν στο λεξικό, το σύνολο αυτών των λέξεων, το word count του λεξικού, το word count του script και τους συνολικούς χρόνους για κάθε μία από τις προαναφερόμενες functions. (οι χρόνοι υπολογίζονται με την χρήση της getrusage() ) Ενδεικτικό output (χωρίς τις "misspelled" λέξεις) WORDS MISSPELLED: 17845WORDS IN DICTIONARY: 143091WORDS IN TEXT: 1150970TIME IN load: 0.05TIME IN check: 0.42TIME IN size: 0.00TIME IN unload: 0.02TIME IN TOTAL: 0.48 Τα παραπάνω αποτελέσματα που πέτυχα είναι λίγο καλύτερα από αυτά που πετυχαίνει το εκτελέσιμο του staff του μαθήματος και είμαι έτοιμος να παραδώσω την εργασία. Θέλοντας όμως να διπλοτσεκάρω τo πρόγραμμα κόβω το λεξικό (large:143091 words) στη μέση και ξανατρέχω το πρόγραμμα με το ίδιο script αλλά το μισό λεξικό (med: 71516 words)... και εκεί που περιμένω να δώ μικρότερους χρόνους ο χρόνος του "check" σχεδόν διπλασιάζεται (?) ενώ το performance της εφαρμογής του staff είναι σταθερό WORDS MISSPELLED: 621866WORDS IN DICTIONARY: 71516WORDS IN TEXT: 1150970TIME IN load: 0.03TIME IN check: 0.68TIME IN size: 0.00TIME IN unload: 0.01TIME IN TOTAL: 0.72 Μετά από αυτό δοκίμασα άλλη data structure, άλλο τύπο hash, hash table με linked lists (αν και το αρχικό hash-table είχε ελάχιστα collisions) αλλά δεν βελτίωσα το performance για κανένα από τα λεξικά. Πάνω στις δοκιμές που έκανα σκέφτηκα να κρατήσω το output σε txt, ούτε που θυμάμαι γιατί το έκανα αυτό, και παίρνω τα παρακάτω αποτελέσματα τα οποία λογικά(?) περίμενα από την αρχή (το 'λυσα). WORDS MISSPELLED: 621866WORDS IN DICTIONARY: 71516WORDS IN TEXT: 1150970TIME IN load: 0.03TIME IN check: 0.35TIME IN size: 0.00TIME IN unload: 0.01TIME IN TOTAL: 0.38 Παίρνω και το output του staff σε αρχείο και είναι και αυτό βελτιωμένο, πάλι όμως "κερδάω". Κάτι δεν μ' αρέσει και ξανατρέχω το πρόγραμμα με output στην κονσόλα, οι χρόνοι μου είναι πάλι αυξημένοι του staff σχεδόν σταθεροί. Κάποιος παίζει μαζί μου; Είναι δυνατό τα printf στο stdout να χαλούν τόσο πολύ μόνο τους δικούς μου χρόνους; όλο το πρόγραμμα στο link ( δεν επιτρέπεται να πειράξουμε το speller.c και τα declarations των functions, το check πρέπει να είναι not case sensitive και το strlen(dic_word) είναι max 44, επίσης οι λέξεις στο λεξικό άποτελούνται εγγυημένα μόνο από γράμματα ή από γράμματα και απόστροφο ). https://github.com/gifour/learning-c-exercises.git Ευχαριστώ και μόνο που το διαβάσατε
kaliakman Δημοσ. 20 Σεπτεμβρίου 2016 Δημοσ. 20 Σεπτεμβρίου 2016 Καλημέρα, Με μια γρήγορη ματιά που είδα κανείς πολύ μεγάλη δέσμευση μνήμης για το hash table( 4 φορές πάνω από το large set σου) χωρίς να γίνεται resize και γενικά με την υλοποίηση σου μπορεί να δημιουργηθούν μεγάλες λίστες και χάνεις ταχύτητα στο ψάξιμο. Δες λίγο στα hash table τι παίζει με load factor και rehashing. Κατά το μεσημέρι που θα είμαι σε υπολογιστή θα σου στείλω και ένα paper που με βοήθησε να καταλάβω τι συμβαίνει στα hash table. 1
imitheos Δημοσ. 20 Σεπτεμβρίου 2016 Δημοσ. 20 Σεπτεμβρίου 2016 Θέλοντας όμως να διπλοτσεκάρω τo πρόγραμμα κόβω το λεξικό (large:143091 words) στη μέση και ξανατρέχω το πρόγραμμα με το ίδιο script αλλά το μισό λεξικό (med: 71516 words)... και εκεί που περιμένω να δώ μικρότερους χρόνους ο χρόνος του "check" σχεδόν διπλασιάζεται (?) ενώ το performance της εφαρμογής του staff είναι σταθερό Πέρα από τις βελτιώσεις του κώδικα που πρότεινε ο kaliakman (δεν κοίταξα τον κώδικά σου) τι έκδοση compiler χρησιμοποιείς ? Με το δικό σου Makefile δηλαδή clang με ggdb και O3 παίρνω ακριβώς ίδιους χρόνους. Επίσης δοκίμασα O2 (γιατί μερικές φορές το O3 οδηγεί σε μείωση επιδόσεων αντί για αύξηση) και χωρίς debbuging symbols και πάλι οι χρόνοι είναι ίδιοι. % for i in holmes.txt shakespeare.txt; do for k in large med; do echo $i ./speller $k $i |grep ' ' done done holmes.txt MISSPELLED WORDS WORDS MISSPELLED: 17845 WORDS IN DICTIONARY: 143091 WORDS IN TEXT: 1150970 TIME IN load: 0.07 TIME IN check: 0.31 TIME IN size: 0.00 TIME IN unload: 0.01 TIME IN TOTAL: 0.39 holmes.txt MISSPELLED WORDS WORDS MISSPELLED: 621866 WORDS IN DICTIONARY: 71516 WORDS IN TEXT: 1150970 TIME IN load: 0.01 TIME IN check: 0.29 TIME IN size: 0.00 TIME IN unload: 0.01 TIME IN TOTAL: 0.31 shakespeare.txt MISSPELLED WORDS WORDS MISSPELLED: 45691 WORDS IN DICTIONARY: 143091 WORDS IN TEXT: 904612 TIME IN load: 0.03 TIME IN check: 0.25 TIME IN size: 0.00 TIME IN unload: 0.01 TIME IN TOTAL: 0.29 shakespeare.txt MISSPELLED WORDS WORDS MISSPELLED: 499120 WORDS IN DICTIONARY: 71516 WORDS IN TEXT: 904612 TIME IN load: 0.01 TIME IN check: 0.24 TIME IN size: 0.00 TIME IN unload: 0.01 TIME IN TOTAL: 0.26 % make gcc -ggdb3 -O3 -std=c11 -Wall -Werror -c -o speller.o speller.c gcc -ggdb3 -O3 -std=c11 -Wall -Werror -c -o dictionary.o dictionary.c gcc -ggdb3 -O3 -std=c11 -Wall -Werror -o speller speller.o dictionary.o % for i in holmes.txt shakespeare.txt; do for k in large med; do echo $i ./speller $k $i |grep ' ' done done holmes.txt MISSPELLED WORDS WORDS MISSPELLED: 17845 WORDS IN DICTIONARY: 143091 WORDS IN TEXT: 1150970 TIME IN load: 0.08 TIME IN check: 0.31 TIME IN size: 0.00 TIME IN unload: 0.01 TIME IN TOTAL: 0.39 holmes.txt MISSPELLED WORDS WORDS MISSPELLED: 621866 WORDS IN DICTIONARY: 71516 WORDS IN TEXT: 1150970 TIME IN load: 0.01 TIME IN check: 0.32 TIME IN size: 0.00 TIME IN unload: 0.01 TIME IN TOTAL: 0.34 shakespeare.txt MISSPELLED WORDS WORDS MISSPELLED: 45691 WORDS IN DICTIONARY: 143091 WORDS IN TEXT: 904612 TIME IN load: 0.03 TIME IN check: 0.23 TIME IN size: 0.00 TIME IN unload: 0.01 TIME IN TOTAL: 0.27 shakespeare.txt MISSPELLED WORDS WORDS MISSPELLED: 499120 WORDS IN DICTIONARY: 71516 WORDS IN TEXT: 904612 TIME IN load: 0.01 TIME IN check: 0.21 TIME IN size: 0.00 TIME IN unload: 0.01 TIME IN TOTAL: 0.23 Και πάλι ίδιοι χρόνοι με τον gcc. % clang --version clang version 3.8.1 (tags/RELEASE_381/final) Target: x86_64-unknown-linux-gnu Thread model: posix % gcc --version gcc (GCC) 5.4.0
gifour Δημοσ. 20 Σεπτεμβρίου 2016 Μέλος Δημοσ. 20 Σεπτεμβρίου 2016 @imitheos Είτε με gcc είτε με clang έχω κι εγώ το ίδιο αποτέλεσμα. compilers version [email protected]<script data-cfhash='f9e31' type="text/javascript">/* */</script> (~/pset5): clang --version Ubuntu clang version 3.4-1ubuntu3 (tags/RELEASE_34/final) (based on LLVM 3.4) Target: i386-pc-linux-gnu Thread model: posix [email protected]/* */ (~/pset5): gcc --version gcc (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4 το θέμα είναι πως υπάρχει μεγάλη διαφοροποίηση στους χρόνους, η οποία δείχνει να εξαρτάται από το αν το output πάει στην κονσόλα ή όχι. Το φαινόμενο είναι εντονότερο στο med λεξικό. ./speller large holmes.txt ./speller large holmes.txt >> holmesout.txt (έχει τυπώσει 17845 lines (έχει τυπώσει 17845 lines στην κονσόλα) αλλά στο holmesout.txt) WORDS MISSPELLED: 17845 WORDS MISSPELLED: 17845 WORDS IN DICTIONARY: 143091 WORDS IN DICTIONARY: 143091 WORDS IN TEXT: 1150970 WORDS IN TEXT: 1150970 TIME IN load: 0.05 TIME IN load: 0.06 TIME IN check: 0.50 TIME IN check: 0.42 TIME IN size: 0.00 TIME IN size: 0.00 TIME IN unload: 0.02 TIME IN unload: 0.02 TIME IN TOTAL: 0.58 TIME IN TOTAL: 0.50 -------------------------------------------------------------------------------- ./speller med holmes.txt ./speller med holmes.txt >> holmesout.txt (έχει τυπώσει 621866 lines (έχει τυπώσει 621866 lines στην κονσόλα) αλλά στο holmesout.txt) WORDS MISSPELLED: 621866 WORDS MISSPELLED: 621866 WORDS IN DICTIONARY: 143091 WORDS IN DICTIONARY: 143091 WORDS IN TEXT: 1150970 WORDS IN TEXT: 1150970 TIME IN load: 0.03 TIME IN load: 0.04 TIME IN check: 0.67 TIME IN check: 0.41 TIME IN size: 0.00 TIME IN size: 0.00 TIME IN unload: 0.01 TIME IN unload: 0.01 TIME IN TOTAL: 0.71 TIME IN TOTAL: 0.45 Δείχνει να υπάρχει κάποιο bottleneck που οφείλεται ... στον stdout(?).
defacer Δημοσ. 20 Σεπτεμβρίου 2016 Δημοσ. 20 Σεπτεμβρίου 2016 Η κονσόλα γενικά είναι κάτι notoriously slow όταν βγάζεις πολλή έξοδο. Μπορεί ακόμα και να καταλήξει σε voodoo ιστορίες όπως αυτή εδώ που τη βρήκα αρκετά διασκεδαστική. Δε βλέπω τίποτα το ανεξήγητο σ' αυτό που σου συμβαίνει. 1
imitheos Δημοσ. 20 Σεπτεμβρίου 2016 Δημοσ. 20 Σεπτεμβρίου 2016 Ubuntu clang version 3.4-1ubuntu3 (tags/RELEASE_34/final) (based on LLVM 3.4) Όπως σου λέει ο defacer, η κονσόλα είναι πολύ αργή και γενικά οι i/o λειτουργίες είναι πολύ αργές για αυτό και ποτέ δεν λαμβάνονται υπόψην στα benchmarks. Επίσης, ασχέτως με το παρόν πρόβλημα, δοκίμασε να βάλεις νέα έκδοση clang. Ήθελα και πριν να σου πω ότι από ένα σημείο και πριν ο clang παρήγαγε πολύ χειρότερο κώδικα από τον gcc αλλά δεν θυμόμουν ακριβώς ποια έκδοση ήταν (έχω την εντύπωση ότι ήταν μέχρι την 3.6 ή την 3.5) για αυτό περίμενα να διαβάσω ποια έκδοση έχεις πρώτα. Έχει γίνει βέβαια καταπληκτική δουλειά και τα προβλήματα σιγά εξαλήφθηκαν και οι τελευταίες εκδόσεις είναι πολύ καλές. Αν το ubuntu δίνει 3.8.1 (ή ακόμη και την πρόσφατη 3.9), βάλε την οπωσδήποτε. 1
gifour Δημοσ. 20 Σεπτεμβρίου 2016 Μέλος Δημοσ. 20 Σεπτεμβρίου 2016 @kaliakman η χρήση μνήμης που γίνεται δεν φαίνεται να είναι απαγορευτική από valgrind HEAP SUMMARY: ==2183== in use at exit: 0 bytes in 0 blocks ==2183== total heap usage: 143,094 allocs, 143,094 frees, 11,693,092 bytes allocated ==2183== ==2183== All heap blocks were freed -- no leaks are possible ==2183== ==2183== For counts of detected and suppressed errors, rerun with: -v ==2183== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) η υλοποίηση του "ανταγωνιστή" HEAP SUMMARY: ==3794== in use at exit: 0 bytes in 0 blocks ==3794== total heap usage: 367,083 allocs, 367,083 frees, 41,113,536 bytes allocated ==3794== ==3794== All heap blocks were freed -- no leaks are possible ==3794== ==3794== For counts of detected and suppressed errors, rerun with: -v ==3794== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) Σημείωση: το ζητούμενο στην άσκηση είναι η ταχύτητα. @defacer @imitheos Συμφωνώ, αλλά η επίδραση της κονσόλας θα πρέπει λογικά(?) να είναι η ίδια ( ή σχεδόν η ίδια) για παρόμοιες εφαρμογές που τρέχουν στο ίδιο σύστημα. Στη δική μου περίπτωση δεν ισχύει όμως αυτό καθώς η δική μου εφαρμογή καθυστερεί ενώ η εφαρμογή που δίνεται από το μάθημα ως μέτρο σύγκρισης δεν δέχεται τόσο σημαντικό impact. Επίσης έχω δοκιμάσει να κάνω το compile με gcc χωρίς να δω κάτι διαφορετικό.
_Gikoskos_ Δημοσ. 20 Σεπτεμβρίου 2016 Δημοσ. 20 Σεπτεμβρίου 2016 Κοιταξα λιγο τον κωδικα σου και μπορω να πω πως αν οντως τωρα αρχιζεις να μαθαινεις C γραφεις πολυ καλογραμμενο κωδικα. Καποιες παρατηρησεις. Στο πρωτο ποστ λες πως αυτο ειναι μια απο τις λειτουργιες του API σου: - "load" που φορτώνει το φάκελο .txt του λεξικού στη μνήμη, Ομως απ'οτι βλεπω εσυ δεν φορτωνεις το txt στην μνημη αλλα απλα ανοιγεις ενα file descriptor και διαβαζεις κανονικα το αρχειο απο τον δισκο με το fopen/fread API: https://github.com/gifour/learning-c-exercises/blob/master/dictionary.c#L72 Για να φορτωσεις το αρχειο στην μνημη οπως γραφεις (στην RAM δηλαδη) θα επρεπε να χρησιμοποιήσεις file mapping με το mmap API σε unixοειδη συστηματα ή με συνδυασμο των CreateFileMapping και MapViewOfFile σε WinAPI. Η εκδοση του WinAPI ειναι καπως πιο περιπλοκη απο το mmap γιατι πρεπει να ρυθμισεις περισσοτερα πραματα για το mapping σου. Κατα πασα πιθανοτητα αν κανεις mapping το αρχειο σου, και αντι να το διαβαζεις με την fread απλα το διαβαζες απο εναν buffer (στην μορφη πινακα) τοτε η συναρτηση load θα ηταν τραγικα γρηγορη. Επισης κατι αλλο. Οταν διαβαζεις αρχειο απο τον δισκο, δεν ειναι καλο να διαβαζεις byte με byte. Συνηθως ο δισκος λειτουργει πιο γρηγορα αν διαβαζεις μια μεγαλη ποσοτητα δεδομενων μαζεμενη, αντι για ενα byte καθε φορα. Αυτη η ποσοτητα bytes ονομαζεται block. Στους περισσοτερους σκληρους ενα block ειναι πολλαπλασιο των 512 bytes. Αυτο σημαινει πως το να διαβαζεις 512 bytes μαζι ειναι πολυ πολυ πιο γρηγορο (σε πολυ μεγαλυτερο ποσοστο μαλιστα) απο το να διαβαζεις 1 byte καθε φορα. Οι λογοι γι' αυτην την αρχιτεκτονικη ειναι πολλοι. Αν σε ενδιαφερει ψαξτο περισσοτερο. Βεβαια αυτο ισχυει κατα κανονα σε unbuffered Ι/Ο χαμηλου επιπεδου (οπως το open/read/write Unix API). Σε υψηλοτερου επιπεδου APIs οπως ειναι το fopen/fread/fwrite συνηθως υπαρχει ενα στανταρ μεγεθος buffer που χρησιμοποιηται καθε φορα. Αυτο σημαινει πως δεν εχει σημασια ποσα bytes πεις εσυ στην συναρτηση να διαβασει ή να γραψει. Αυτη παντα θα τα διαβαζει ή θα τα γραφει μεσα σε εναν buffer και μονο οταν ο buffer γεμισει, θα κληθει μια απο τις συναρτησεις read/write για να γραφτουν/διαβαστουν τα δεδομενα στο/απο το αρχειο. Αυτο ονομαζεται buffered I/O. Ο buffer αυτος, συνηθως στις υλοποιήσεις, ειναι μελος της δομης FILE. Μπορει να ειναι ενας δυναμικος πινακας της μορφης: typedef struct _FILE { ... char *buf; ... } FILE; όπου μπορει να γινεται malloc στο μεγεθος BUFSIZ (που ειναι συνηθως το μεγεθος ενος block) οταν καλεις fopen ή την πρωτη fwrite/fread. Για να αλλαξεις τον τυπο buffering που γινεται σε ενα FILE* χρησιμοποιησε την συναρτηση setvbuf. Δηλαδη μπορεις να ρυθμισεις αν θελεις οι fread/fwrite να γραφουν/διαβαζουν δεδομενα ανα ενα byte (η επιλογη _IONBF, unbuffered δηλαδη), ανα γραμμή (_IOLBF line buffered που ειναι και ο τυπος buffering που γινεται σε εισοδο/εξοδο στο τερματικο, δηλαδη μεχρι να συναντηθει το πρωτο '\n') και ανα block (_IOFBF fully buffered). Μια καλη ασκηση ειναι να δοκιμασεις να γραψεις δικες σου εκδοσεις καποιων συναρτησεων της στανταρ βιβλιοθηκης της C. Μαλλον ξεφυγα λιγο, αλλα γενικα να ξερεις πως το I/O ειναι πολυ παραξενο προβλημα στους υπολογιστες. Δηλαδη θα απαντησεις πολλα ευτραπελα αν γραφεις εφαρμογες που κανουν εντονη χρηση I/O. Για παραδειγμα, οι χρονοι σου μπορει να ειναι διαφορετικοι με τον ιδιο κωδικα σε αλλο λειτουργικο συστημα και στον ιδιο υπολογιστη, οπου διαφερει η υλοποιηση του fopen/fread/fwrite API της στανταρ βιβλιοθηκης. edit: BUFSIZ ειναι η μακροεντολη που οριζεται στην Standard C, οχι BUFSIZE 3
imitheos Δημοσ. 20 Σεπτεμβρίου 2016 Δημοσ. 20 Σεπτεμβρίου 2016 το θέμα είναι πως υπάρχει μεγάλη διαφοροποίηση στους χρόνους, η οποία δείχνει να εξαρτάται από το αν το output πάει στην κονσόλα ή όχι. Το φαινόμενο είναι εντονότερο στο med λεξικό. Δείχνει να υπάρχει κάποιο bottleneck που οφείλεται ... στον stdout(?). Το φαινόμενο είναι εντονότερο στο med λεξικό γιατί το πρόγραμμά σου τυπώνει τις mis-spelled λέξεις οπότε στην μία περίπτωση τυπώνει 17Κ λέξεις ενώ στην άλλη περίπτωση 621K λέξεις. Πολύ περισσότερες εκτυπώσεις στην κονσόλα => πολύς περισσότερος χρόνος. Όπως είδες, όταν το βάζεις στο αρχείο large και med έχουν ίδιο χρόνο. Στη δική μου περίπτωση δεν ισχύει όμως αυτό καθώς η δική μου εφαρμογή καθυστερεί ενώ η εφαρμογή που δίνεται από το μάθημα ως μέτρο σύγκρισης δεν δέχεται τόσο σημαντικό impact. Το πρόγραμμά-μέτρο σύγκρισης της άσκησης εμφανίζει τις mis-spelled λέξεις ? Που είναι ο κώδικάς του να το δούμε ?
gifour Δημοσ. 20 Σεπτεμβρίου 2016 Μέλος Δημοσ. 20 Σεπτεμβρίου 2016 Κοιταξα λιγο τον κωδικα σου και μπορω να πω πως αν οντως τωρα αρχιζεις να μαθαινεις C γραφεις πολυ καλογραμμενο κωδικα. Μόνο το dictionary.c είναι δικό μου. Τα υπόλοιπα τα έδινε η άσκηση. Στο συγκεκριμένο course δίνoυν μεγάλη σημασία στο readability και προσπαθώ να ακολουθώ τη γραμμή. Ομως απ'οτι βλεπω εσυ δεν φορτωνεις το txt στην μνημη αλλα απλα ανοιγεις ενα file descriptor και διαβαζεις κανονικα το αρχειο απο τον δισκο με το fopen/fread API: https://github.com/gifour/learning-c-exercises/blob/master/dictionary.c#L72 Για να φορτωσεις το αρχειο στην μνημη οπως γραφεις (στην RAM δηλαδη) θα επρεπε να χρησιμοποιήσεις file mapping με το mmap API σε unixοειδη συστηματα ή με συνδυασμο των CreateFileMapping και MapViewOfFile σε WinAPI. Η εκδοση του WinAPI ειναι καπως πιο περιπλοκη απο το mmap γιατι πρεπει να ρυθμισεις περισσοτερα πραματα για το mapping σου. Κατα πασα πιθανοτητα αν κανεις mapping το αρχειο σου, και αντι να το διαβαζεις με την fread απλα το διαβαζες απο εναν buffer (στην μορφη πινακα) τοτε η συναρτηση load θα ηταν τραγικα γρηγορη. Ναι ανοίγω τον file descriptor και μετά διαβάζω από εκεί char per char τις λέξεις τερματίζω τα strings με '\0', υπολογίζω τoν hashindex και ανάλογα με αυτόν αντιγράφω το string σε allocated memory. Αυτό εννούσα "φορτώνει στη μνήμη" δεν ήξερα πως αλλιώς να το πω, υπάρχει κάτι πιο συγκεκριμένο; Αυτό με το mapping δεν το ξέρω. Θα το ψάξω, όπως επίσης και το buffered I/O. να περιμένω και το paper για τα hash-tables που μου έταξες; Το πρόγραμμά-μέτρο σύγκρισης της άσκησης εμφανίζει τις mis-spelled λέξεις ? Που είναι ο κώδικάς του να το δούμε ? Αυτό το δίνουν σε εκτελέσιμο με κλειστό το debug. Αν νομίζεις πως αξίζει τον κόπο, να το ανεβάσω με τα υπόλοιπα αρχεία. cs50speller, ανέβηκε στο https://github.com/g...c-exercises.git
kaliakman Δημοσ. 20 Σεπτεμβρίου 2016 Δημοσ. 20 Σεπτεμβρίου 2016 [...] Εγώ στο έταξα Site: Link1 (Οι πρώτες σελίδες σε ενδιαφέρουν) Site: Link2 Το πρώτο έχει ένα μικρό λάθος στην υλοποιήση από ότι είχαμε βρε αλλά δεν έχει νοήμα να σου πω αν δεν το διαβάσεις λίγο για να σου εξηγήσω 1
gifour Δημοσ. 21 Σεπτεμβρίου 2016 Μέλος Δημοσ. 21 Σεπτεμβρίου 2016 Μπερδεύτικα λίγο με τα posts. ok, ευχαριστώ για όλα τα links που δώσατε.
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα