Alithinos Δημοσ. 25 Απριλίου 2016 Μέλος Δημοσ. 25 Απριλίου 2016 Ωραία μου φαίνεται τα λες, αλλά υπάρχει ένα πρόβλημα: δεν έχω μάθει ακόμα τα interfaces. Το μόνο interface που ξέρω είναι το User Interface, το οποίο μου φαίνεται διαφορετικό από αυτά τα interfaces. Μου φαίνεται πως ίσως άμα προχωρήσω λίγο ακόμα, θα μπορέσω να καταλάβω καλύτερα για τους indexers, από τα υπόλοιπα που θα 'συμπληρώσουν την εικόνα του παζλ'. Thanx πάντως. υ.γ. Έχω αρχίσει να γράφω μια λίστα σε ένα αρχείο .txt με πράγματα που όταν τα συναντώ στο βιβλίο από όπου μαθαίνω, δεν τα καταλαβαίνω απόλυτα. Το σκεπτικό είναι ότι θα τους ρίξω ξανά μια ματιά όταν τελειώσω το βιβλίο, πιστεύοντας πως τότε όλα τα υπόλοιπα που έχω μάθει θα με βοηθήσουν να καταλάβω αυτά που έχω αφήσει απ' έξω καλύτερα. Να για παράδειγμα ένα άλλο θέμα που άφησα στην άκρη ήταν οι πράξεις σε κάθε bit ξεχωριστά (bitwise operations).
defacer Δημοσ. 25 Απριλίου 2016 Δημοσ. 25 Απριλίου 2016 Καλά μη νομίζεις ότι και γω περιμένω πως αυτά που γράφω αφομοιώνονται με τη μία. Το προηγούμενο είναι σίγουρα πέρα από το επίπεδό σου αυτή τη στιγμή αν μη τι άλλο επειδή κινείται σχεδόν εξολοκλήρου σε χωράφια αφηρημένων εννοιών. Ο σκοπός μου είναι να συνδέσω αυτά τα πράγματα στο μυαλό σου με όρους που σου είναι ήδη γνώριμοι, κι απο κει και πέρα αν έχω κάνει καλά τη δουλειά μου θα επιστρέψεις στο μέλλον και θα κάνεις ένα καλύτερο κοσκίνισμα, πιθανόν περισσότερες από μία φορές, μέχρι να φτάσεις στο επίπεδο να το διαβάζεις νεράκι και να καταλαβαίνεις ακριβώς τι συζητάμε. 1
Alithinos Δημοσ. 27 Απριλίου 2016 Μέλος Δημοσ. 27 Απριλίου 2016 Άρα καλώς τα κρατάω στην άκρη για αργότερα. Έχω παρατηρήσει, ότι πλέον ο τύπος των αποριών μου έχει αλλάξει. Αρχικά ρώταγα ερωτήσεις του τύπου "τι κάνει αυτό" ή "πως το κάνω αυτό", ενώ πλέον οι περισσότερες απορίες μου είναι του τύπου "γιατί να το κάνω αυτό ;" Πλέον όταν μαθαίνω κάτι νέο μου φαίνεται πιο εύκολο να καταλάβω τι κάνει, αλλά δε βρίσκω αμέσως το λόγο που να με κάνει να προτιμήσω να υλοποιήσω κάτι έτσι και όχι αλλιώς. Πάρε για παράδειγμα μια απορία που μου γεννήθηκε σήμερα, όπου τις τελευταίες 3 μέρες έχω αρχίσει και διαβάζω για τη κληρονομικότητα. Σήμερα έμαθα το μάθημα για τις virtual & override μεθόδους. Ξέρω όμως ήδη πως μπορώ να κάνω αυτό: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication4 { class Program { static void Main(string[] args) { Παράγωγο2 αντικείμενο = new Παράγωγο2(); αντικείμενο.ShowContents(); Console.ReadKey(); } } class Βάση { protected int a = 5; public void ShowContents() { Console.WriteLine(a); } } class Παράγωγο : Βάση { protected int b = 10; new public void ShowContents() { Console.WriteLine("{0} {1}",base.a, ; } } class Παράγωγο2 : Παράγωγο { int c = 15; new public void ShowContents() { Console.WriteLine("{0} {1} {2}",this.a,this.b,c); } } } Άρα, γιατί να το κάνω έτσι: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication4 { class Program { static void Main(string[] args) { Παράγωγο2 αντικείμενο = new Παράγωγο2(); αντικείμενο.ShowContents(); Console.ReadKey(); } } class Βάση { protected int a = 5; public virtual void ShowContents() { Console.WriteLine(a); } } class Παράγωγο : Βάση { protected int b = 10; public override void ShowContents() { Console.WriteLine("{0} {1}",base.a, ; } } class Παράγωγο2 : Παράγωγο { int c = 15; public override void ShowContents() { Console.WriteLine("{0} {1} {2}",this.a,this.b,c); } } } Το αποτέλεσμα που θα τυπωθεί και στις 2 περιπτώσεις είναι το ίδιο: 5 10 15 Το πρόβλημα όμως με αυτά που καταλαβαίνω τι παν να κάνουν αλλά δε βλέπω το λόγο για τον οποίο θα έπρεπε να τα χρησιμοποιήσω, είναι πως μου είναι πιο δύσκολο να εξασκηθώ με αυτά. Και αυτό γιατί ακριβώς δεν ξέρω που θα τα χρησιμοποιήσω, και έτσι δεν μου έρχεται πάντα κάποια ιδέα για να φτιάξω ένα προγραμματάκι που να κάνει χρήση αυτών των νέων πραγμάτων, ώστε να τα δω πως δουλεύουν στη πράξη! Και άμα προχωρήσω το βιβλίο χωρίς να κάτσω να εξασκηθώ πρακτικά σε κάτι, να κάτσω να γράψω ένα πρόγραμμα όπου θα γίνεται χρήση αυτού που έμαθα, για να το δω να δουλεύει στη πράξη, νιώθω πως κλέβω τον εαυτό μου, και ότι κινδυνεύω ότι έμαθα να το ξεχάσω μετά από κάποιο καιρό. Τόσο που σκέφτομαι αφού τελειώσω το βιβλίο αυτό, να βρω ένα άλλο βιβλίο ακριβώς για την ίδια γλώσσα, και να το αρχίσω και εκείνο από την αρχή, για να κάνω επανάληψη και να ξαναθυμηθώ πράγματα που ίσως να έχω ξεχάσει, και μήπως κάποια πράγματα μου τα παρουσιάσει με διαφορετικό τρόπο, και έτσι τα καταλάβω καλύτερα...
albNik Δημοσ. 27 Απριλίου 2016 Δημοσ. 27 Απριλίου 2016 Τρέξε αυτό για να δεις τη διαφορά override-new. public class A { public virtual void F1() { Console.WriteLine("1"); } public void F2() { Console.WriteLine("2"); } } public class B : A { public override void F1() { Console.WriteLine("3"); } public new void F2() { Console.WriteLine("4"); } } ... ... A a = new B(); B b = new B(); a.F1(); // 3 b.F1(); // 3 a.F2(); //2!!!!! b.F2(); //4 1
Alithinos Δημοσ. 27 Απριλίου 2016 Μέλος Δημοσ. 27 Απριλίου 2016 Άρα η διαφορά είναι για όταν έχουμε μεταβλητή αναφοράς σε διαφορετικό τύπο από το τύπο του αντικειμένου ? Επειδή προσπαθούμε να αναθέσουμε στην a που είναι τύπου A μια Β, έτσι η a θα κρατήσει μόνο τα μέλη που περιέχει η Α. Έτσι αντί να τρέξει την F2 της Β, θα τρέξει την F2 της Α. Ενώ η b επειδή είναι τύπου B και η μεταβλητή αναφοράς και η μεταβλητή που της αναθέτουμε, θα εκτελέσει τη F2 της Β. Αυτό όμως δεν θα μπορούσαμε να το αποφύγουμε τελείως ? Για παράδειγμα να γράφαμε A a = new A(); ή B a = new B(); ανάλογα πως θα θέλαμε να συμπεριφερθεί η a. Μου φαίνεται σαν αλχημεία για να λύσει ένα πρόβλημα που αν κάποιος είναι προσεχτικός κατά την ανακοίνωση της μεταβλητής δεν θα χρειαζόταν καν..
groot Δημοσ. 27 Απριλίου 2016 Δημοσ. 27 Απριλίου 2016 χε... Design είναι (πάλι) η λέξη που λείπει. Σε πολλές διαδικασίες δεν σε νοιάζει ο συγκεκριμένος τύπος δεδομένων αλλά ένας γενικότερος. Π.χ. κάποιος είχε αναφέρει για ένα γκαράζ και αμάξια. Εσένα δεν σε νοιάζει εάν είναι νταλίκα ή λιμουζίνα. Σε νοιάζει ότι είναι αμάξι. Ή, και κατά το δημοφιλές παράδειγμα με τους υπαλλήλους, σε νοιάζει ότι είναι υπάλληλος. Όχι τι υπάλληλος ακριβώς είναι. Π.χ. εάν θες να κάνεις "πρόσληψη". 1
albNik Δημοσ. 27 Απριλίου 2016 Δημοσ. 27 Απριλίου 2016 Αυτό όμως δεν θα μπορούσαμε να το αποφύγουμε τελείως ? Για παράδειγμα να γράφαμε A a = new A(); ή B a = new B(); Αν ειχες μια λιστα απο Animals και δεν ηξερες αν το καθενα ειναι Cat, Dog κλπ δεν ξερεις ποια Run θα εκτελεστεί. foreach(Animal animal in list) animal.Run(); Γενικα το "new" δεν θυμάμαι να το εχω χρησιμοποιήσει ποτέ. 1
Papakaliati Δημοσ. 27 Απριλίου 2016 Δημοσ. 27 Απριλίου 2016 Γενικως το new δεν πρεπει να το χρησιμοποιησεις ποτε, μιας και δεν θα πρεπει ποτε να "καλυψεις" μια μεθοδο που δεν ειναι virtual. Απλα ξεχνα οτι υπαρχει και προχωρα με το abstract. Μετα απο καιρο, μπορεις να το επισκεφτεις παλι το θεμα ωστε να καταλαβεις και απο μονος σου γιατι θα πρεπει να το αποφευγεις. Θα καταλαβεις οτι ειναι εξαιρετικα δυσαναγνωστος ο κωδικας, εχοντας διαφορετικη υλοποιηση για μια μεθοδο που δεν ειναι virtual, και επομενως δεν περιμενεις να εχει διαφορετικη υλοποιηση. 1
defacer Δημοσ. 27 Απριλίου 2016 Δημοσ. 27 Απριλίου 2016 Άρα η διαφορά είναι για όταν έχουμε μεταβλητή αναφοράς σε διαφορετικό τύπο από το τύπο του αντικειμένου ? Βασικά ναι. Υπάρχουν δύο πράγματα εδώ που είναι ξεχωριστά μεταξύ τους αν και φαίνεται πως σχετίζονται πολύ: Πώς γίνεται το binding του method call Πώς γίνεται το dispatch του method call Binding: συμβαίνει at compile time. Δηλαδή, όταν δει ο compiler μια παράσταση του στυλ foo.bar(), σε ποιά ακριβώς bar() αναφέρεται ο κώδικας; Προσοχή, αυτό είναι διαφορετικό από το τι ακριβώς πρέπει να καλέσουμε για να ικανοποιηθεί η κλήση της bar. Το binding εξαρτάται από το static type του foo, δηλαδή από το guarantee που έχει ο compiler για το τι αντικείμενο είναι, και τους τυχόν ορισμούς της bar() πάνω σε αυτό το type και τυχόν bases του. Dispatch: αυτό γίνεται ενδεχομένως at compile time, ενδεχομένως at runtime. Δηλαδή, ξέροντας πως με το foo.bar() αναφερόμασταν συγκεκριμένα στην public void bar() της class Foo, ποιόν ακριβώς κώδικα πρέπει να τρέξουμε για να ικανοποιήσουμε το call? Το dispatch εξαρτάται από τον ορισμό της Foo.bar και (ενδεχομένως, αν αυτή είναι virtual), από το dynamic type του foo (που μπορεί να είναι Foo ή κάτι derived από αυτό). Δεν έχω χρόνο τώρα οπότε δες αν θες αυτό και μπορούμε να συζητήσουμε τι ακριβώς συμβαίνει εκεί αργότερα. Γενικά μιλώντας η χρήση του new για να καπελώσεις μια base method (λέγεται "shadowing") είναι πολύ advanced feature και στην πράξη απίθανο να χρειαστεί. 1
Alithinos Δημοσ. 30 Απριλίου 2016 Μέλος Δημοσ. 30 Απριλίου 2016 I see... Θα πρέπει να χρησιμοποιώ virtual / override τις περισσότερες των περιπτώσεων, για να μπορώ να αναθέσω σε μια μεταβλητή αναφοράς τύπου base class μια τιμή τύπου derived class, επειδή μπορεί να προκύψει κατάσταση όπου δεν έχω αποφασίσει / γνωρίζω από πριν τι τύπου derived class θα θέλω να είναι το συγκεκριμένο στιγμιότυπο. Έτσι θα ήταν καλύτερο να υιοθετήσω τη χρήση virtual / override ως συνήθεια, εκτός από ειδικές περιπτώσεις. Ευχαριστώ πολύ παιδιά για τις απαντήσεις!
Alithinos Δημοσ. 3 Μαΐου 2016 Μέλος Δημοσ. 3 Μαΐου 2016 Το είπες μόνος σου, "κάποια" προβλήματα. Όχι όλα. Η αναδρομή είναι όντως πιο βαριά στη γενική περίπτωση. Πώς προκύπτει αυτό; Απλά, συγκρίνεις το τι πρέπει να γίνει για να "πάρει μπρος" η επόμενη επανάληψη. Στην περίπτωση του loop τυπικά (σε επίπεδο assembly) θα έχεις τρεις εντολές: μια increment, μια σύγκριση και μια conditional jump (πάει στην αρχή του loop ή απλά συνεχίζει παρακάτω ανάλογα το αποτέλεσμα της σύγκρισης). Στην περίπτωση της αναδρομής ξεκινάς πρώτα απ' όλα με ένα function call, το οποίο σημαίνει ότι πρέπει να μπουν στο stack οι τιμές των κατάλληλων registers της CPU, στη συνέχεια αυτοί οι registers να πάρουν νέες τιμές, να μεγαλώσει το stack, ενδεχομένως να γίνει αρχικοποίηση local μεταβλητών στο καινούριο stack frame, μετά ένα jump στην αρχή του κώδικα της συνάρτησης, κι έχει ο θεός. Αυτό κοστίζει όχι μόνο σε χρόνο αλλά και σε χώρο στο stack -- εξ ου και αν βάλεις μια συνάρτηση που καλεί συνέχεια τον εαυτό της, σύντομα τελειώνει ο διαθέσιμος χώρος για το stack και καταλήγεις στο γνωστό stack overflow. Αυτό βέβαια γενικά. Ειδικά, μια συνάρτηση που είναι tail recursive μπορεί να μετατραπεί από τον compiler σε ακριβές ισοδύναμο ενός loop (θυμήσου, ο compiler μπορεί να κάνει "ό,τι του αρέσει" αρκεί το αποτέλεσμα όπως φαίνεται σε έναν εξωτερικό παρατηρητή που έχει σα μόνη αναφορά το εκάστοτε spec της γλώσσας να είναι ακριβώς το ίδιο που θα ήταν αν είχε κάνει "ακριβώς ο,τι του είπες"). Υπάρχουν γλώσσες όπου ο compiler τυπικά το κάνει επειδή μπορεί (C++), άλλες που δε μπαίνει στον κόπο αν και θα μπορούσε (C#), άλλες που το spec εγγυάται το tail recursion όταν είναι εφικτό (Scheme). TL;DR: use the right tool for the job. Και τα δύο, στην προκειμένη περίπτωση βέβαια αντικειμενικά μεγαλύτερο ρόλο παίζει το δεύτερο. The right tool for the job. Η μέθοδος με αναδρομή είναι για εξάσκηση, όπως και το να κάνεις πολλαπλασιασμούς manually. 1. Δεν έχει καμία σχέση με την κουβέντα ο αντικειμενοστραφής, μη μπερδεύεις έννοιες. 2. Η επαναχρησιμοποίηση κώδικα είναι κάτι γενικά επιθυμητό. Επίσης δε σχετίζεται με την κουβέντα, ούτε και με το ΟΟ συγκεκριμένα. Στη συγκεκριμένη περίπτωση γίνομαι προβλέψιμος, αλλά δεν υπάρχει απολύτως κανένας λόγος να χρησιμοποιήσεις αναδρομή στην πράξη. Το κάνεις για εξάσκηση. Και αν το σκεφτείς είναι προεξοφλημένο πως εφόσον αυτού του είδους η εξάσκηση για να γίνει σωστά πρέπει να κάνεις κάτι που ήδη ξέρεις και καταλαβαίνεις απλά με άλλο τρόπο, και αυτό που ήδη ξέρεις και καταλαβαίνεις γίνεται καλύτερα με επανάληψη αφού δεν έχεις διδαχτεί νωρίτερα αναδρομή (και άρα τα πράγματα που γίνονται καλύτερα μ' αυτή), προφανώς το τελικό αποτέλεσμα από πρακτικής άποψης είναι suboptimal. Καλώς το παλικάρι που ως συνήθως δεν ξέρει ή δε μπορεί να εκφράσει τι είναι reentrancy. Reentrancy δεν είναι κάτι που συμβαίνει. Είναι μια ιδιότητα που είτε έχει είτε δεν έχει κάποια συγκεκριμένη συνάρτηση. Βρήκα σήμερα άλλο ένα τρόπο που η αναδρομή είναι χρήσιμη. Για αντικατάσταση του goto. Ξεκίνησα να μαθαίνω για τα exceptions, και είπα να φτιάξω μια μέθοδο που δέχεται έναν αριθμό από το χρήστη και χρησιμοποιεί try-catch για να σιγουρευτεί ότι ο χρήστης έβαλε int (παλιότερα το έκανα με if / else). Ήθελα όμως άμα γίνει το exception, να επαναλαμβάνεται η μέθοδος ξανά. Αρχικά την έγραψα έτσι: static int GetNumber() { GetNumberStart: try { Console.WriteLine("Give me the number: "); return Convert.ToInt32(Console.ReadLine()); } catch { Console.WriteLine("Input was not correct.\n"); goto GetNumberStart; } } Μετά όμως σκέφτηκα αυτό που λεν, ότι το goto είναι κακό, παρότι δεν είχα καταλάβει ακριβώς γιατί να είναι κακό. Αποφάσισα λοιπόν να βρω τρόπο να το αντικαταστήσω, και θυμήθηκα την αναδρομή που είχαμε συζητήσει νωρίτερα. Έτσι έκανα αυτή την αλλαγή: static int GetNumber() { try { Console.WriteLine("Give me the number: "); return Convert.ToInt32(Console.ReadLine()); } catch { Console.WriteLine("Input was not correct.\n"); return GetNumber(); // Αντί για GOTO χρησιμοποίησα αναδρομή! } } Και είδα στη πράξη πως απλοποιήθηκε ο κώδικας. Από κει που πριν έπρεπε να κοιτάξω 2 γραμμές για να καταλάβω τι γίνεται (τη γραμμή που "καλείται" το goto και ύστερα να ψάξω να βρω που είναι η δήλωσή του), τώρα απλά κοιτάω μια γραμμή (return GetNumber(). Χάρηκα που επιτέλους μου δόθηκε η ευκαιρία να μου φανεί χρήσιμο κάτι που έμαθα και έβαλα αμέσως 'στην άκρη' γιατί δεν έβρισκα περιπτώσεις χρήσης. 1
defacer Δημοσ. 3 Μαΐου 2016 Δημοσ. 3 Μαΐου 2016 Θα πρέπει να χρησιμοποιώ virtual / override τις περισσότερες των περιπτώσεων, για να μπορώ να αναθέσω σε μια μεταβλητή αναφοράς τύπου base class μια τιμή τύπου derived class, επειδή μπορεί να προκύψει κατάσταση όπου δεν έχω αποφασίσει / γνωρίζω από πριν τι τύπου derived class θα θέλω να είναι το συγκεκριμένο στιγμιότυπο. Έτσι θα ήταν καλύτερο να υιοθετήσω τη χρήση virtual / override ως συνήθεια, εκτός από ειδικές περιπτώσεις. Βασικά όχι, αλλά μη σε προβληματίζει πάρα πολύ αυτή τη στιγμή. Το αν θα κάνεις κάτι virtual ή όχι είναι θέμα σχεδιασμού (design). Όπως είναι και το αν θα κάνεις κάτι sealed (δεν το έχουμε αναφέρει αλλά θα το συναντήσεις). Αυτό λοιπόν που λες για συνήθεια, είναι στην πραγματικότητα κακή συνήθεια επειδή βασίζεται στο φόβο του αγνώστου και όχι στη σιγουριά ενός σωστά καταστρωμένου σχεδίου. Το αντίστοιχο θα ήταν ας πούμε να λέγαμε πως θα βάλουμε στο κινητό που σχεδιάζουμε αποσπώμενη μπαταρία επειδή πού ξέρεις, μπορεί κάπου να χρησιμεύσει. Τελείως λάθος λογική. Ή ξέρεις ακριβώς πού θα χρησιμεύσει και σχεδιάζεις ανάλογα και την υπόλοιπη συσκευή για να στοχεύσεις εκείνο το κοινό, ή αλλιώς έκανες το κινητό σου μεγαλύτερο χωρίς πραγματικό λόγο. Anyway μη σε σκοτίζει αυτό που λέω γιατί είναι νωρίς ακόμα, αλλά κράτα το εξής: η χρήση των language features πρέπει να είναι αποτέλεσμα σκέψης και όχι αβεβαιότητας. Μετά όμως σκέφτηκα αυτό που λεν, ότι το goto είναι κακό, παρότι δεν είχα καταλάβει ακριβώς γιατί να είναι κακό. Αποφάσισα λοιπόν να βρω τρόπο να το αντικαταστήσω, και θυμήθηκα την αναδρομή που είχαμε συζητήσει νωρίτερα. Χάρηκα που επιτέλους μου δόθηκε η ευκαιρία να μου φανεί χρήσιμο κάτι που έμαθα και έβαλα αμέσως 'στην άκρη' γιατί δεν έβρισκα περιπτώσεις χρήσης. Τέλεια! Αυτό που έκανες είναι πρακτικά πατάτα, αλλά είναι πολύ θετικό το γεγονός ότι λειτούργησες όπως λειτούργησες και βρήκες μια διαφορετική, ξεχωριστή λύση στο πρόβλημα που είχες μπροστά σου. Κράτα αυτή τη νοοτροπία και τα υπόλοιπα είναι "λεπτομέρειες": θα μάθεις με τον καιρό να αναγνωρίζεις μόνος σου τις πατάτες και ιδανικά οι ξεχωριστές σου λύσεις θα γίνουν ξεχωριστά καλές. Στην προκειμένη έγινε το κλασικό "when all you have is a hammer everything looks like a nail": είχες άχτι να κάνεις κάτι με αναδρομή και έκανες. Και μετά έγινε το άλλο κλασικό από το σκάκι: βρήκες μια "καλή" κίνηση και σταμάτησες να ψάχνεις οπότε δε βρήκες την ακόμα καλύτερη, που φανερά εδώ είναι απλά να βάλεις τα πάντα μέσα σε while: static int GetNumber() { while (true) { try { Console.WriteLine("Give me the number: "); return Convert.ToInt32(Console.ReadLine()); } catch // ποτέ μη κάνεις γενικό catch, πως λέμε ποτέ μη χρησιμοποιείς goto { Console.WriteLine("Input was not correct.\n"); } } } Σαν επιπλέον tip: question everything. Όταν λέμε "ποτέ goto", πρέπει eventually να καταλάβεις γιατί. Στην αρχή φυσικά πολλά πράγματα είναι πέρα από τον ορίζοντά σου οπότε κολλάει το κλασικό "όταν έρθει η ώρα να παραβείς τον κανόνα θα το καταλάβεις μόνος σου", αλλά για να αποκτήσεις mastery σε οτιδήποτε πρέπει να το κάνεις κτήμα σου: να μπορείς εσύ να εξηγείς με παραδείγματα πότε είναι κακό και πότε όχι τόσο κακό. Για να το κάνεις αυτό συνήθως πρέπει να μάθεις the hard way. Οπότε θα σου συνιστούσα να αρχίσεις να χρησιμοποιείς περισσότερο goto στον κώδικά σου. Και όταν έρθει η στιγμή που η κατάσταση θα αρχίσει να σε κάνει να δυσανασχετείς, κάτσε κάτω και σκέψου με την ησυχία σου γιατί δυσανασχετείς και πώς θα ήταν καλύτερα τα πράγματα. Ακόμα και αν δε μπορείς μόνος σου να βρεις την απάντηση, το να φτάσεις στο σημείο να διατυπώνεις ρητά και ξεκάθαρα τον προβληματισμό σου είναι ένα πάρα πολύ σημαντικό milestone. 1
Alithinos Δημοσ. 4 Μαΐου 2016 Μέλος Δημοσ. 4 Μαΐου 2016 Βασικά όχι, αλλά μη σε προβληματίζει πάρα πολύ αυτή τη στιγμή. Πότε όμως θα πρέπει να αρχίσει να με προβληματίζει ? Όπως είναι και το αν θα κάνεις κάτι sealed (δεν το έχουμε αναφέρει αλλά θα το συναντήσεις). Το συνάντησα, αλλά μου φάνηκε εύκολα αντιληπτή η χρήση του και για αυτό δεν το ανέφερα εδώ. Ορίζουμε μια κλάση ως sealed, όταν θέλουμε να αποτρέψουμε τη χρήση της ως base class από derived classes. Δεν μπορεί μια κλάση να κληρονομήσει από τη κλάση sealed. Το αντίστοιχο θα ήταν ας πούμε να λέγαμε πως θα βάλουμε στο κινητό που σχεδιάζουμε αποσπώμενη μπαταρία επειδή πού ξέρεις, μπορεί κάπου να χρησιμεύσει. Τελείως λάθος λογική. Ή ξέρεις ακριβώς πού θα χρησιμεύσει και σχεδιάζεις ανάλογα και την υπόλοιπη συσκευή για να στοχεύσεις εκείνο το κοινό, ή αλλιώς έκανες το κινητό σου μεγαλύτερο χωρίς πραγματικό λόγο. Anyway μη σε σκοτίζει αυτό που λέω γιατί είναι νωρίς ακόμα, αλλά κράτα το εξής: η χρήση των language features πρέπει να είναι αποτέλεσμα σκέψης και όχι αβεβαιότητας. Χμ οκ, θα περιμένω για αργότερα. Τέλεια! Αυτό που έκανες είναι πρακτικά πατάτα, αλλά είναι πολύ θετικό το γεγονός ότι λειτούργησες όπως λειτούργησες και βρήκες μια διαφορετική, ξεχωριστή λύση στο πρόβλημα που είχες μπροστά σου. Κράτα αυτή τη νοοτροπία και τα υπόλοιπα είναι "λεπτομέρειες": θα μάθεις με τον καιρό να αναγνωρίζεις μόνος σου τις πατάτες και ιδανικά οι ξεχωριστές σου λύσεις θα γίνουν ξεχωριστά καλές. Στην προκειμένη έγινε το κλασικό "when all you have is a hammer everything looks like a nail": είχες άχτι να κάνεις κάτι με αναδρομή και έκανες. Και μετά έγινε το άλλο κλασικό από το σκάκι: βρήκες μια "καλή" κίνηση και σταμάτησες να ψάχνεις οπότε δε βρήκες την ακόμα καλύτερη, που φανερά εδώ είναι απλά να βάλεις τα πάντα μέσα σε while: Χμμμ... Θα είναι καλύτερο να χρησιμοποιούσα τη while επειδή ο βρόχος είναι πιο γρήγορος, γιατί η αναδρομή πρέπει να κάνει εκ νέου κλήση, όπως είχαμε πει νωρίτερα ε ? Ναι δε πήγε εκεί το μυαλό μου. Μου πέρασε η ιδέα να χρησιμοποιήσω βρόχο, αλλά δεν την ανάλυσα και αρκετά, και μόλις σε δευτερόλεπτα μετά σκέφτηκα την αναδρομή, αποφάσισα αμέσως να χρησιμοποιήσω αναδρομή, γιατί όπως είπαμε βρήκα ένα σημείο που μου φάνηκε χρήσιμη. Απλά νομίζω πως θα ήταν καλύτερα πρώτα να φτιάξω κάτι που να δουλεύει, να ολοκληρώσω τη λειτουργικότητα, και βεβαίως με ένα τρόπο που να είναι ευδιάβαστος όσο γίνεται ο κώδικας για να μην ταλαιπωρούμαι κατά την ολοκλήρωση της λειτουργικότητας, και πως το optimization για να βρω όχι μόνο έναν τρόπο που να δουλεύει και να είναι ευανάγνωστος, αλλά και γρήγορος / χρησιμοποιώντας λιγότερη μνήμη, να το αφήνω για το τέλος. Αφού έχω καταφέρει να κάνω τα πάντα να λειτουργούν, και ιδιαίτερα μάλιστα αν προκύπτουν αποτελέσματα προβλήματος (πχ απ τη στιγμή που όταν τρέχει η εφαρμογή πατηθεί το τάδε, άμα πάρει παραπάνω από Χ χρόνο, πρέπει να ξανακοιτάξω, και να αλλάξω πράγματα). Αν όμως δεν έχω ρητές απαιτήσεις για τις ανάγκες του συστήματος, δηλαδή δεν μου πει κάποιος πχ (πρέπει max το πρόγραμμα να τρώει τόσα MB) ή κάτι παρόμοιο, αξίζει να ξοδεύω τον παραπάνω χρόνο που απαιτείται για να το κάνω περαιτέρω optimize ? Κάποια πράγματα, μπορούν να "κρυφτούν κάτω απ' το χαλί", πχ ένας μέσος χρήστης δεν θα καταλάβει καμία διαφορά αν το πρόγραμμα από κει που του έπαιρνε 0.09s για να του δώσει απάντηση του έπαιρνε 0.023s. (Ανάλογα το πρόγραμμα φυσικά) Άρα η απορία θα μπορούσε να οριστεί ως εξής: Είναι προτιμότερο να σπαταλήσω τον παραπάνω χρόνο για να κάνω το κώδικα όσο πιο optimized γίνεται, ή είναι καλύτερα να γράψω κώδικα ο οποίος δουλεύει μεν, και είναι εύκολα αναγνώσιμος, αλλά θα μπορούσε να γραφτεί με τρόπο που η εκτέλεση του να είναι αρκετά πιο γρήγορη, για να γλιτώσω χρόνο ? Σαν επιπλέον tip: question everything. Όταν λέμε "ποτέ goto", πρέπει eventually να καταλάβεις γιατί. Στην αρχή φυσικά πολλά πράγματα είναι πέρα από τον ορίζοντά σου οπότε κολλάει το κλασικό "όταν έρθει η ώρα να παραβείς τον κανόνα θα το καταλάβεις μόνος σου", αλλά για να αποκτήσεις mastery σε οτιδήποτε πρέπει να το κάνεις κτήμα σου: να μπορείς εσύ να εξηγείς με παραδείγματα πότε είναι κακό και πότε όχι τόσο κακό. Για να το κάνεις αυτό συνήθως πρέπει να μάθεις the hard way. Οπότε θα σου συνιστούσα να αρχίσεις να χρησιμοποιείς περισσότερο goto στον κώδικά σου. Και όταν έρθει η στιγμή που η κατάσταση θα αρχίσει να σε κάνει να δυσανασχετείς, κάτσε κάτω και σκέψου με την ησυχία σου γιατί δυσανασχετείς και πώς θα ήταν καλύτερα τα πράγματα. Ακόμα και αν δε μπορείς μόνος σου να βρεις την απάντηση, το να φτάσεις στο σημείο να διατυπώνεις ρητά και ξεκάθαρα τον προβληματισμό σου είναι ένα πάρα πολύ σημαντικό milestone. Χμ θα το προσπαθήσω. Αν και ήδη έχω ψιλο-αντιληφθεί κάτι, όπως στο παράδειγμα που έδωσα. Γιατί άμα βάλεις πολλά goto, θα πρέπει να ψάχνεις από δω και από κει κάθε φορά που θες να διαβάσεις ένα κομμάτι, για να βρεις που είναι το σημείο στο οποίο θα σε πάει το goto. Και έτσι ξοδεύεις χρόνο ψάχνοντας από δω και από κει, χρόνο που θα μπορούσες να τον χρησιμοποιήσεις κάνοντας κάτι άλλο. Το οποίο goto είναι απομεινάρι από μια εποχή όπου τα προγράμματα δεν είχαν καν κλάσεις και ήταν ολόκληρα σε ένα κατεβατό / αρχείο, ενώ τώρα έχουμε ποια μεθόδους και κλάσεις, και μπορούμε να πηγαινοφέρνουμε το βήμα της εκτέλεσης πέρα δόθε χωρίς τέτοιους προϊστορικούς τρόπους!
M2000 Δημοσ. 5 Μαΐου 2016 Δημοσ. 5 Μαΐου 2016 Το goto είναι πολύ χρήσιμο. Πχ σε πολλαπλά if else θα ήθελες κάπου να συνεχίσεις στη Β διακλάδωση...ενώ έχεις πάρει την Α. Η λύση της συνάρτησης δεν σε ικανοποιεί. Με ένα απλό goto συνεχίζεις. Δεν σημαίνει να το βάζουμε όπου λάχει.
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα