Προς το περιεχόμενο

Συνάρτηση που επιστρέφει τη θέση του στοιχείου ενός πίνακα


lektikos

Προτεινόμενες αναρτήσεις

πχ.

...
// Διαβάζεις στα σοιχεία που σου δίνει στο πληκτρολόγιο μέσα στον elems
...
// Διαβάζεις ποιο στοιχείο θέλεις να ψάξει να σου βρει μέσα στον πίνακα elems
...

if (lookup_element( int elem, int elems[MAXELEMS], bool posfound[MAXELEMS] ) )
{
   // Βρήκε το elem μέσα στον elems και τυπώνει τις θέσεις που το βρήκε από το posfounds
   // Αν όλα τα στοιχεία του posfound είναι false δεν βρέθηκε στο elem μεσα στο elems
}
else
{
   // Κάπου υπήρξε σφάλμα στην συνάρτηση
}
...

στην ουσία το elem είναι το δικό σου num και το elems το δικό σου nums...

 

EDIT:

@migf1: Θα ήταν λάθος πρακτική να επιστρέφει η lookup_element false σε περίπτωση που δεν θα έβρισκε το στοιχείο μέσα στον πίνακα; Με τη λογική να γλυτώσεις έναν έλεγχο στη main για το αν εχει κάποιο true στοιχείο ο posfound ώστε να τυπώσεις τη/τις θέση/εις ή να βγάλεις μήνυμα ότι δεν βρέθηκε;

(καταλαβαίνω ότι αν επέστρεφε false δεν μπορούμε να ξεχωρίσουμε αν δεν βρήκε το στοιχείο ή έγινε κάποιο λάθος μέσα, απλά ρωτάω γιατί όταν πήγα να γράψω το παράδειγμα το πρώτο που μου ήρθε στο μυαλό ήταν να βάλω:

if (lookup_element(...) ) 
{
   // τύπωσε τις θέσεις
}
else
{
   //Δεν βρέθηκε το στοιχείο
}

)

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

  • Απαντ. 70
  • Δημ.
  • Τελ. απάντηση

Συχνή συμμετοχή στο θέμα

Δημοφιλείς Ημέρες

Συχνή συμμετοχή στο θέμα

...

EDIT:

@migf1: Θα ήταν λάθος πρακτική να επιστρέφει η lookup_element false σε περίπτωση που δεν θα έβρισκε το στοιχείο μέσα στον πίνακα; Με τη λογική να γλυτώσεις έναν έλεγχο στη main για το αν εχει κάποιο true στοιχείο ο posfound ώστε να τυπώσεις τη/τις θέση/εις ή να βγάλεις μήνυμα ότι δεν βρέθηκε;

(καταλαβαίνω ότι αν επέστρεφε false δεν μπορούμε να ξεχωρίσουμε αν δεν βρήκε το στοιχείο ή έγινε κάποιο λάθος μέσα, απλά ρωτάω γιατί όταν πήγα να γράψω το παράδειγμα το πρώτο που μου ήρθε στο μυαλό ήταν να βάλω:

if (lookup_element(...) ) 
{
   // τύπωσε τις θέσεις
}
else
{
   //Δεν βρέθηκε το στοιχείο
}
)

 

 

Καθόλου λάθος δεν θα ήταν. Βασικά εξαρτάται από το τι σε εξυπηρετεί καλύτερα στο εκάστοτε πρότζεκτ σου. Θα μπορούσες επίσης να εντοπίζεις και το ένα και το άλλο, π.χ. με χρήση έξτρα ορίσματος, ή με χρήση του errno από την errno.h.

  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Οι υλοποιήσεις που έχουν ποσταριστεί ως τώρα έχουν εμφανή μειονεκτήματα. Όταν θέλουμε μια καλή υλοποίηση η οποία να μπορεί ταυτόχρονα να απαντήσει γρήγορα στο "υπάρχει το τάδε στοιχείο" όσο και στο "ποιές είναι όλες οι θέσεις όπου εμφανίζεται" η λύση είναι γνωστή και μοναδική: iterator style. Το οποίο σε C θα μπορούσε να είναι ως εξής:

 

int* find_next_equal_to(int* pBegin, int* pEnd, int needle) {
    while(pBegin < pEnd) {
        if (*pBegin == needle) return pBegin;
        ++pBegin;
    }
 
    return 0;
}
 

Χρήση "if exists":

 

int haystack[10] = { 1, 2, 3, 4, 5, 5, 4, 3, 2, 1 };
int needle = 4;
int* pFound;
 
if (pFound = find_next_equal_to(haystack, haystack + 10, needle)) {
    printf("First occurrence: at index %d\n", pFound - haystack);
}
else {
    printf("Not found\n");
}
Χρήση "find all":

int haystack[10] = { 1, 2, 3, 4, 5, 5, 4, 3, 2, 1 };
int needle = 4;
int* pStart = haystack;
    
while (pStart = find_next_equal_to(pStart, haystack + 10, needle)) {
    printf("Found at index %d: %d\n", pStart - haystack, *pStart);
    ++pStart;
}
Από μια τέτοια υλοποίηση μπορεί κανείς να φτιάξει οποιοδήποτε API μπορεί να τον βολεύει στη συνέχεια, ενώ το αντίστροφο δεν ισχύει.

 

Επίσης γενικά μου έβγαλε μάτι το να περνάνε οι πίνακες στην οποιαδήποτε συνάρτηση-υλοποίηση σαν int[], κάτι που σαν μοναδικό πρακτικό αποτέλεσμα έχει το να μειώνεται η επαναχρησιμοποιησιμότητα της συνάρτησης. Προφανώς θα έπρεπε να είναι typed ως int*, άσχετα αν στη συνέχεια και για λόγους API επιλέξει κανείς να περνάει το μέγεθος του range σαν pointer ή σαν int ή να το παίρνει hardcoded.

  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Οι υλοποιήσεις που έχουν ποσταριστεί ως τώρα έχουν εμφανή μειονεκτήματα.

Όπως για παράδειγμα;

 

Όταν θέλουμε μια καλή υλοποίηση η οποία να μπορεί ταυτόχρονα να απαντήσει γρήγορα στο "υπάρχει το τάδε στοιχείο" όσο και στο "ποιές είναι όλες οι θέσεις όπου εμφανίζεται" η λύση είναι γνωστή και μοναδική: iterator style. Το οποίο σε C θα μπορούσε να είναι ως εξής:

 

 

int* find_next_equal_to(int* pBegin, int* pEnd, int needle) {
    while(pBegin < pEnd) {
        if (*pBegin == needle) return pBegin;
        ++pBegin;
    }
 
    return 0;
}

 

Χρήση "if exists":

 

 

int haystack[10] = { 1, 2, 3, 4, 5, 5, 4, 3, 2, 1 };
int needle = 4;
int* pFound;
 
if (pFound = find_next_equal_to(haystack, haystack + 10, needle)) {
    printf("First occurrence: at index %d\n", pFound - haystack);
}
else {
    printf("Not found\n");
}

 

Χρήση "find all":

 

 

int haystack[10] = { 1, 2, 3, 4, 5, 5, 4, 3, 2, 1 };
int needle = 4;
int* pStart = haystack;
    
while (pStart = find_next_equal_to(pStart, haystack + 10, needle)) {
    printf("Found at index %d: %d\n", pStart - haystack, *pStart);
    ++pStart;
}

 

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

 

Στη συγκεκριμένη υλοποίηση που παρουσιάζεις, τι ακριβώς θα συμβεί στην find_next_equal_to() (και σε όλες τις υπόλοιπες που θα βασίζονται σε αυτήν στο API που θα φτιάξεις) αν έστω και κατά λάθος το pBegin περαστεί ως NULL?

 

Επίσης, πως ακριβώς η συγκεκριμένη συνάρτηση ξεχωρίζει τις περιπτώσεις που το needle δεν βρέθηκε στο haystack από την περίπτωση που περάστηκε bad-argument (p.x. NULL)? Σημείωσε πως δεν θα έκανα τέτοιο nitpicking αν 1) δεν χαρακτήριζες τη δική σου υλοποίηση καλύτερη από τις υπόλοιπες στο νήμα και β) αν δεν έλεγες πως πάνω σε αυτήν θα χτίσεις API.

 

Επίσης γενικά μου έβγαλε μάτι το να περνάνε οι πίνακες στην οποιαδήποτε συνάρτηση-υλοποίηση σαν int[], κάτι που σαν μοναδικό πρακτικό αποτέλεσμα έχει το να μειώνεται η επαναχρησιμοποιησιμότητα της συνάρτησης. Προφανώς θα έπρεπε να είναι typed ως int*, άσχετα αν στη συνέχεια και για λόγους API επιλέξει κανείς να περνάει το μέγεθος του range σαν pointer ή σαν int ή να το παίρνει hardcoded.

Προφανώς αγνοείς ότι int[], int[CONSTANT] και *int είναι το ίδιο πράγμα ως όρισμα συνάρτησης.
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Όπως για παράδειγμα;

Είτε δεν κάνουν για να βρεις όλα τα occurrences, είτε σε αναγκάζουν να τα βρεις όλα ακόμα κι αν δεν τα θες. Αυτή που έδωσες με τον bool[] μάλιστα σε αναγκάζει να κάνεις και allocate έναν πίνακα που ενδεχομένως δε σκοπεύεις να χρησιμοποιήσεις. 

 

Στη συγκεκριμένη υλοποίηση που παρουσιάζεις, τι ακριβώς θα συμβεί στην find_next_equal_to() (και σε όλες τις υπόλοιπες που θα βασίζονται σε αυτήν στο API που θα φτιάξεις) αν έστω και κατά λάθος το pBegin περαστεί ως NULL?

Undefined behavior. Αν δε σου αρέσει αυτό βάλε μόνος σου το null check μέσα. Το να συζητάω για διαφορετική προσέγγιση σε επίπεδο API και να μου λές για τα null checks (τα οποία μπαίνουν ή βγαίνουν όπου θες) είναι αστείο. Επίσης, "όλες" οι functions της standard library που δουλεύουν με pointers σου σκάνε στη μούρη αν περάσεις null και αυτό δεν έγινε στην τύχη. Αν θέλεις πήγαινε να παραπονεθείς ότι η strlen(0) δεν επιστρέφει κάτι συγκεκριμένο να δεις τι θα σου πουν.

 

Επίσης, πως ακριβώς η συγκεκριμένη συνάρτηση ξεχωρίζει τις περιπτώσεις που το needle δεν βρέθηκε στο haystack από την περίπτωση που περάστηκε bad-argument (p.x. NULL)?

Αν περάστηκε bad argument τότε (όπως και παντού σε natively compiled γλώσσες στη γενική περίπτωση) πρόβλημα του caller. Η ερώτηση είναι αστεία -- εσένα η δική σου πώς ξεχωρίζει την περίπτωση που το needle δε βρέθηκε από την περίπτωση που πέρασες pointer σε περιοχή μνήμης όπου δεν έχεις read access? Άσε, μην απαντήσεις.

 

Σημείωσε πως δεν θα έκανα τέτοιο nitpicking αν 1) δεν χαρακτήριζες τη δική σου υλοποίηση καλύτερη από τις υπόλοιπες στο νήμα και β) αν δεν έλεγες πως πάνω σε αυτήν θα χτίσεις API.

Η υλοποίηση δεν είναι "δική μου", είναι η ίδια ιδέα που χρησιμοποιείται εκτενώς στη C++, επίσης στη strtok (αν και εκεί το state του iterator είναι -- κακώς -- σε global variable) και σε πάμπολλες άλλες libraries. Επίσης δεν είναι δικό μου το πρόβλημα να δεχτώ ότι υπάρχει καλύτερη προσέγγιση από αυτή που συνέστησα.

 

Προφανώς αγνοείς ότι int[], int[CONSTANT] και *int είναι το ίδιο πράγμα ως όρισμα συνάρτησης.

Χαριτωμένο. Βασικά είχα υπόψη ότι ενώ οι arrays κάνουν decay σε pointers, εντούτοις arrays και pointers είναι διαφορετικοί τύποι και χωρίς να το ψάξω σκέφτηκα ότι ο compiler θα έχει αντίρρηση να του δώσεις έναν int[2] εκεί που περιμένει έναν int[10] (κάτι που όντως γίνεται στη C++ αλλά μόνο όταν μιλάμε για references, μόλις το κοίταξα).

 

Περαστικά.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

@defacer & migf1: Guys, μην αρχίσετε, plz!!!

 

Ο TS απ'ότι καταλαβαίνουμε όλοι, τώρα μαθαίνει τη γλώσσα και ρώτησε γιατί δεν του έδινε το αποτέλεσμα που ήθελε η συνάρτησή του. Η συνάρτησή του δούλευε σωστά (με τις όποιες παραβλέψεις και λαθάκια ενός πρωτάρη) και με κάποιε μικρές υποδείξεις θα μπορούσε να κάνει τη δουλειά του. Δε ζήτησε κώδικα το παιδί αλλά σίγουρα καλό είναι να του επισημαίνουμε σωστές τακτικές.

 

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

 

Ας φτιάξουμε ένα thread με τίτλο "Γαμάτοι κώδικες στη C" και να βάζει εκεί ο καθένας ότι κώδικα θέλει για input manipulation, search, shorting και ότι άλλο θέλεις και να "τους μετράμε" εκεί (τους κώδικες) να δούμε ποιου είναι ο καλύτερος...

 

Καθαρά φιλικά όλα αυτά και με δόση χιούμορ για να μην παρεξηγηθώ.

 

Άντε καληνύχτα γιατί έχω και σχολείο αύριο :P

  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Καθόλου λάθος δεν θα ήταν. Βασικά εξαρτάται από το τι σε εξυπηρετεί καλύτερα στο εκάστοτε πρότζεκτ σου. Θα μπορούσες επίσης να εντοπίζεις και το ένα και το άλλο, π.χ. με χρήση έξτρα ορίσματος, ή με χρήση του errno από την errno.h.

Link.png Site: Οτι καλυτερο για αυτη την δουλεια.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Είτε δεν κάνουν για να βρεις όλα τα occurrences, είτε σε αναγκάζουν να τα βρεις όλα ακόμα κι αν δεν τα θες. Αυτή που έδωσες με τον bool[] μάλιστα σε αναγκάζει να κάνεις και allocate έναν πίνακα που ενδεχομένως δε σκοπεύεις να χρησιμοποιήσεις.

Κι αυτό που έδωσες εσύ σε αναγκάζει να γράψεις έξτρα κώδικα όταν όντως θέλεις να μαζέψεις όλα τα occurrences σε έναν πίνακα. Κοντολογίς, κολοκυθιά!

 

Undefined behavior. Αν δε σου αρέσει αυτό βάλε μόνος σου το null check μέσα. Το να συζητάω για διαφορετική προσέγγιση σε επίπεδο API και να μου λές για τα null checks (τα οποία μπαίνουν ή βγαίνουν όπου θες) είναι αστείο.

Αφού είναι αστείο, και τα null-checks μπαίνουν ή βγαίνουν όπου θες, how come η καλύτερη και μοναδική υλοποίηση να μην τα χρησιμοποιεί;

 

Επίσης, "όλες" οι functions της standard library που δουλεύουν με pointers σου σκάνε στη μούρη αν περάσεις null και αυτό δεν έγινε στην τύχη. Αν θέλεις πήγαινε να παραπονεθείς ότι η strlen(0) δεν επιστρέφει κάτι συγκεκριμένο να δεις τι θα σου πουν.

 

Αν περάστηκε bad argument τότε (όπως και παντού σε natively compiled γλώσσες στη γενική περίπτωση) πρόβλημα του caller. Η ερώτηση είναι αστεία -- εσένα η δική σου πώς ξεχωρίζει την περίπτωση που το needle δε βρέθηκε από την περίπτωση που πέρασες pointer σε περιοχή μνήμης όπου δεν έχεις read access? Άσε, μην απαντήσεις.

Δεν ήμουν εγώ εκείνος που μπήκα στο νήμα να παρουσιάσω τη δική μου υλοποίηση ως την καλύτερη και μοναδική! Ούτε πως αρμόζει για να χτίσεις πάνω της API. Απεναντίας, αυτό που έδωσα το χαρακτήρισα εξαρχής ως εύκολη & γρήγορη λύση για αυτό που ρώτησε ο TS. Πάραυτα, όταν ρωτήθηκα επί τούτου αναφέρθηκα σε έξτρα όρισμα ή σε χρήση του errno (θα μπορούσα να την βάλω να επιστρέφει και struct, ή καποιο bitmap). Κάτι που προφανέστατα θα έκανα αν έφτιαχνα συνάρτηση πάνω στην οποία θα έχτιζα API.

 

Η υλοποίηση δεν είναι "δική μου", είναι η ίδια ιδέα που χρησιμοποιείται εκτενώς στη C++, επίσης στη strtok (αν και εκεί το state του iterator είναι -- κακώς -- σε global variable) και σε πάμπολλες άλλες libraries. Επίσης δεν είναι δικό μου το πρόβλημα να δεχτώ ότι υπάρχει καλύτερη προσέγγιση από αυτή που συνέστησα.

Αυτό που έδωσες είναι ακριβής προσαρμογή σε int αντί για char της strchr(), χρησιμοποιείται δηλαδή από την εποχή των δεινοσαύρων, πολύ-πολύ πριν δηλαδή τη C++. Και είναι όντως μια προσέγγιση, δεν είναι ούτε η καλύτερη ούτε η μοναδική.

 

Χαριτωμένο. Βασικά είχα υπόψη ότι ενώ οι arrays κάνουν decay σε pointers, εντούτοις arrays και pointers είναι διαφορετικοί τύποι και χωρίς να το ψάξω σκέφτηκα ότι ο compiler θα έχει αντίρρηση να του δώσεις έναν int[2] εκεί που περιμένει έναν int[10] (κάτι που όντως γίνεται στη C++ αλλά μόνο όταν μιλάμε για references, μόλις το κοίταξα).

 

Περαστικά.

Άρχισες πάλι τα περί C++ σε νήμα της C και τα γνωστά σου εριστικά, μιας και στα της C δεν σου βγαίνει. Οκ, καταλαβαίνω!
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

@bird: Δεν ξέρω αν έχεις καταλάβει ότι κάποιοι χαίρονται όταν τους "βελτιώνουν" ενώ κάποιοι άλλοι ζορίζονται. Είναι σημαντική η διαφορά. Απο κει και πέρα σκοπός μου δεν είναι να δείξω τίποτα παραπάνω από τον καλύτερο τρόπο που ξέρω για να μη χρειαστεί ο TS και ο κάθε TS να ξαναανακαλύψει μόνος του τον τροχό, αλλά βέβαια δεν είσαι υποχρεωμένος να με πιστέψεις. :-)

 

Τέλος δεν θα υπάρξει καμία αντιπαράθεση, πρώτον γιατί been there done that moving on και δεύτερον γιατί μόλις πεις τη μαγική λέξη Parsifal θα δεις ότι ακόμα και οι πιο σκληροπυρηνικοί μαζεύονται. Να ορίστε δες:

 

"Parsifal".

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

[link=http://en.wikipedia.org/wiki/HRESULT]Οτι καλυτερο για αυτη την δουλεια.

Ναι (είναι αυτό που ανέφερα ως "bitmap" προηγουμένως). Στη C ο στάνταρ βασικός μηχανισμός είναι το errno.h που είναι προφανώς πολύ πιο "πρωτόγονος" από το HRESULT, αλλά δουλεύει καλά σε πολλές περιπτώσεις.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Κι αυτό που έδωσες εσύ σε αναγκάζει να γράψεις έξτρα κώδικα όταν όντως θέλεις να μαζέψεις όλα τα occurrences σε έναν πίνακα. Κοντολογίς, κολοκυθιά!

Δείχνεις να μην αντιλαμβάνεσαι τη διαφορά ανάμεσα στο "γράφω extra κώδικα για να μπορώ να κάνω δύο διαφορετικά πράγματα κατά πώς θα μου αρέσει" και στο "ο κώδικας κάνει παραπάνω δουλειά απ' ότι χρειάζεται όταν τον τρέχεις". Αν εσύ το θεωρείς καλύτερο μπορείς να γράφεις και μονολιθικές συναρτήσεις που σου δίνουν την τυπική απόκλιση του μέσου όρου των αθροισμάτων που βρίσκεις μέσα σ' ένα πολυδιάστατο πίνακα. Ή μπορείς να το κάνεις με επαναχρησιμοποιήσιμα μικρότερα κομμάτια. Έχεις τόσο πολλές γνώσεις που είμαι βέβαιος πως ξέρεις τι είναι καλύτερο.

 

Αφού είναι αστείο, και τα null-checks μπαίνουν ή βγαίνουν όπου θες, how come η καλύτερη και μοναδική υλοποίηση να μην τα χρησιμοποιεί;

Επειδή θεωρώ πως είναι καλύτερη προσέγγιση, και αν για δικούς του λόγους ο caller θέλει να περνάει null pointer και να βγαίνει από την άλλη παρδαλό κατσίκι, μπορεί να γράψει ένα wrapper μιας γραμμής που να το κάνει. By default σε τέτοιες περιπτώσεις επιλέγω να αφήσω το πρόγραμμα να κρασάρει (http://pragmatictips.com/32) παίρνοντας μάλιστα παράδειγμα από τη standard library όπως ανέφερα και πριν με το παράδειγμα της strlen. By the way αν δεν έχεις διαβάσει το βιβλίο, να το διαβάσεις.

 

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

 

Δεν ήμουν εγώ εκείνος που μπήκα στο νήμα να παρουσιάσω τη δική μου υλοποίηση ως την καλύτερη και μοναδική! Ούτε πως αρμόζει για να χτίσεις πάνω της API. Απεναντίας, αυτό που έδωσα το χαρακτήρισα εξαρχής ως εύκολη & γρήγορη λύση για αυτό που ρώτησε ο TS. Πάραυτα, όταν ρωτήθηκα επί τούτου αναφέρθηκα σε έξτρα όρισμα ή σε χρήση του errno (θα μπορούσα να την βάλω να επιστρέφει και struct, ή καποιο bitmap). Κάτι που προφανέστατα θα έκανα αν έφτιαχνα συνάρτηση πάνω στην οποία θα έχτιζα API.

Και γω δεν είμαι εκείνος που βγάζει συμπεράσματα χωρίς να ξέρει ή να καταλαβαίνει. Εκτός κι αν διάβασες τη σκέψη μου, οπότε έλα εντάξει μια πλάκα κάναμε, δεν το εννοούσα.

 

Αυτά τα περι errno και ιστορίες για αγρίους αν θέλεις εξήγησέ τα λίγο περισσότερο: μια συνάρτηση που βρίσκει στοιχεία σε έναν πίνακα τι failure modes μπορεί να έχει; Και πώς θα τα ξεχωρίσεις;

 

Αυτό που έδωσες είναι ακριβής προσαρμογή σε int αντί για char της strchr(), χρησιμοποιείται δηλαδή από την εποχή των δεινοσαύρων, πολύ-πολύ πριν δηλαδή τη C++. Και είναι όντως μια προσέγγιση, δεν είναι ούτε η καλύτερη ούτε η μοναδική.

Φαντάζομαι πως αν σου ζητήσω να μου πεις μια "καλύτερη" οι απαντήσεις θα πέσουν βροχή. Επίσης, ο ίδιος κάνεις το point μου: δεν είναι "δική μου" ιδέα, είναι μια εξαιρετική και σε βάθος χρόνου δοκιμασμένη ιδέα.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δείχνεις να μην αντιλαμβάνεσαι τη διαφορά ανάμεσα στο "γράφω extra κώδικα για να μπορώ να κάνω δύο διαφορετικά πράγματα κατά πώς θα μου αρέσει" και στο "ο κώδικας κάνει παραπάνω δουλειά απ' ότι χρειάζεται όταν τον τρέχεις". Αν εσύ το θεωρείς καλύτερο μπορείς να γράφεις και μονολιθικές συναρτήσεις που σου δίνουν την τυπική απόκλιση του μέσου όρου των αθροισμάτων που βρίσκεις μέσα σ' ένα πολυδιάστατο πίνακα. Ή μπορείς να το κάνεις με επαναχρησιμοποιήσιμα μικρότερα κομμάτια. Έχεις τόσο πολλές γνώσεις που είμαι βέβαιος πως ξέρεις τι είναι καλύτερο.

Αντιλαμβάνομαι τα πάντα, ή τουλάχιστον πολύ περισσότερα από όσα ενδεχομένως νομίζεις.

 

Και πιο συγκεκριμένα, το "καλημέρα" σου στο νήμα...

 

Οι υλοποιήσεις που έχουν ποσταριστεί ως τώρα έχουν εμφανή μειονεκτήματα. Όταν θέλουμε μια καλή υλοποίηση η οποία να μπορεί ταυτόχρονα να απαντήσει γρήγορα στο "υπάρχει το τάδε στοιχείο" όσο και στο "ποιές είναι όλες οι θέσεις όπου εμφανίζεται" η λύση είναι γνωστή και μοναδική: iterator style.

Τα υπόλοιπα αυτού σου του ποστ (και ιδίως τις "υποδείξεις") τα αντιπαρέρχομαι συνειδητά γιατί δεν αξίζει να χαλάω άλλο τη ζαχαρένια μου.

 

Απλώς αν και όταν βρεις διάθεση και κέφι ψάξε να βρεις τη διαφορά μεταξύ API και library, και ξανα διάβασε μετά τα posts σου.

 

EDIT:

 

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

 

...

Αυτά τα περι errno και ιστορίες για αγρίους αν θέλεις εξήγησέ τα λίγο περισσότερο: μια συνάρτηση που βρίσκει στοιχεία σε έναν πίνακα τι failure modes μπορεί να έχει; Και πώς θα τα ξεχωρίσεις;

...

Όταν σου περνάνε NULL ορίσματα ή όταν π.χ. στη δική σου πρόταση το pEnd είναι μικρότερο του pBegin (ή αν περνάς και array length το pEnd είναι μεγαλύτερο του pBegin + length), θέτεις το errno σε EFAULT. Οπότε ο caller έχει τη δυνατότητα να ελέγξει την τιμή του errno μετά την κλήση της συνάρτησής σου για να δει αν η αποτυχία οφείλεται σε bad address ή όχι. Ιδιαίτερα χρήσιμο για debugging ή για recovery.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Sigh.

Θα σχολιάσω την τελευταία παράγραφο με αλλαγμένη σειρά των προτάσεων γιατί έτσι στρώνει καλύτερα το συμπέρασμα.
 

Οπότε ο caller έχει τη δυνατότητα να ελέγξει την τιμή του errno μετά την κλήση της συνάρτησής σου για να δει αν η αποτυχία οφείλεται σε bad address ή όχι. Ιδιαίτερα χρήσιμο για debugging...

 

Με άλλα λόγια, μας λες ότι κάνεις debugging και ότι έχεις ήδη καταλάβει πως έχουμε "αποτυχία" (δηλαδή επέστρεψε null χωρίς να πρέπει) αλλά δεν μπορείς από το σημείο αυτό και ενώ είσαι ήδη στον debugger να δεις αν πέρασες μέσα null pointer ή γενικότερα άλλα αντ' άλλων.

Δε νομίζω ότι έχει νόημα να σχολιάσω περισσότερο.
 

...ή για recovery.

 

Ορίστε; Τι recovery ακριβώς; Θα κάνεις runtime "recovery" από λογικά σφάλματα που έχει το πρόγραμμά σου (προφανώς αφού φτάσαμε να μιλάμε για την περίπτωση που περνάς μέσα λάθος ορίσματα) ή με γελούν τ' αυτιά μου; Αν το κάνεις, απ' όσο ξέρω θα είναι παγκόσμια πρώτη. Ίσως να σε πρόλαβαν αυτοί που γράφουν software για διαστημικές αποστολές, αλλά δεν είμαι και σίγουρος.

 

---

Τώρα, για να το δούμε από λίγο πιο μακριά, αυτό που καταλαβαίνω εδώ είναι ότι καταπατάς κατάφωρα τη διαχωριστική γραμμή ανάμεσα στην παραβίαση ενός precondition (που προέρχεται από 100% λογικό σφάλμα στον κώδικα) και στον έλεγχο για error conditions τα οποία δεν είναι υπό τον έλεγχό σου σύμφωνα με τον προσανατολισμό ή τις ανάγκες της εκάστοτε συνάρτησης.

 

Για παράδειγμα, είναι πολύ λογικό να πεις πως "precondition της strlen είναι πως ο pointer που θα της περάσεις θα είναι pointer σε string και όχι βλακείες" (κάτι που εμμέσως πλην σαφώς αναφέρεται στο standard, όπου βέβαια δεν υπάρχει καμία αναφορά στο errno) ενώ αντίθετα error condition είναι το ότι η function δεν μπόρεσε να επιτελέσει το έργο της για λόγους που δεν περνάνε απαραίτητα από το χέρι ούτε του caller ούτε της ίδιας (για παράδειγμα η tan και η fputs, οι οποίες το standard αναφέρει ότι μπορεί να κάνουν set το errno). Επίσης error condition μπορείς να πεις ότι προκύπτει ενδεχομένως σε functions οι οποίες δεν πραγματοποιούν "αυτοτελή βήματα": μια function η οποία κατεβάζει κάτι από το internet και το γράφει σε ένα αρχείο θα πρέπει να έχει ένα τρόπο να σου πει σε περίπτωση αποτυχίας αν δε μπόρεσε να κατεβάσει από το internet ή αν δε μπόρεσε να γράψει το αρχείο.

 

Η function για την οποία συζητάμε φανερά δεν έχει κανένα failure mode: αν της δώσεις valid όρισμα, σου δίνει πάντα valid αποτέλεσμα. Αυτό ισχύει είτε το "valid" (δηλαδή το precondition) περιλαμβάνει το null pointer όπως λες εσύ είτε δεν το περιλαμβάνει όπως προτείνω εγώ. Επομένως το να βάζουμε το errno μέσα στη συζήτηση είναι απλά λάθος.

 

Ξαναγυρνάω στα περι debugging μιας που το έβαλες μέσα στην κουβέντα: όπως σίγουρα κάποιος γνώστης σαν και σένα θα ξέρει, το να παραπονεθείς επειδή σου έδωσαν 100% λάθος arguments στη C είναι βασικά ο λόγος για τον οποίο υπάρχουν (από την εποχή των δεινοσαύρων; δεν είμαι σίγουρος) assertions. Ορίστε κι ένα παράδειγμα αν το προτιμάς με εικόνες.

 

---

 

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

 

Component Object Model (COM) is a binary-interface standard for software components introduced by Microsoft in 1993. It is used to enable interprocess communication and dynamic object creation in a large range of programming languages.

 

 

Είναι προφανές πως όταν μιλάμε για IPC, ακόμα και η int one() { return 1; } μπορεί να έχει πολλαπλά failure modes -- εκεί έχει νόημα να χρησιμοποιείς error codes. Το να συζητάμε να χρησιμοποιήσουμε στο αυτοκίνητο μια λύση που σχεδιάστηκε έχοντας υπόψη τα προβλήματα που μπορεί να βγάλει ένα ελικόπτερο επειδή αυτή δουλεύει καλά στο ελικόπτερο είναι... άτοπο.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Επειδή τα πολλά λόγια είναι φτώχεια (ειδικά όταν έχω απέναντί μου εσένα) το δικό σου API φτιάξτο όπως είναι η standard string library της C (η πλέον infamous πηγή bugs σε software στην ιστορία της πληροφορικής) και άσε με εμένα τα δικά μου APIs να τα φτιάχνω με προαιρετικό verbose/debugging mode και με ειδοποίηση για recovery σε όποιον άγριο τα χρησιμοποιήσει.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Τα δικά σου μπορείς να τα φτιάχνεις όπως θέλεις, δε θυμάμαι ποτέ να σχολίασα τα ίδια ακριβώς πράγματα που κάνεις στις διάφορες library functions σου που έχεις ποστάρει κατά καιρούς ούτε και να σου υπέδειξα τι κώδικα να γράψεις (LOL όμως ε: το "recovery" έγινε τώρα "ειδοποίηση για recovery" -- λίγο ακόμα και θα καταφέρουμε να το προσγειώσουμε στην άσχημη πραγματικότητα του "τυπώνω error message").

 

Για να λέμε την αλήθεια, εσύ είσαι αυτός που άρχισε να ζητάει τα ρέστα για null checks και με ανάγκασε ηθικά στο να γράψω τα παραπάνω. Την επόμενη φορά αντί να ασχοληθώ μάλλον θα κάνω copy paste αυτά που μόλις έγραψες, αδίκως θιγμένε ήρωα.

 

Cheers.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα

  • Δημιουργία νέου...