Μιας και κάθε ιστορία πρέπει να έχει μια αρχή ας ξεκινήσουμε με ένα απλό πρόβλημα.
Διάβασε ένα αρχείο κειμένου, βρες τις n λέξεις με την μεγαλύτερη συχνότητα, και τύπωσε την λίστα αυτή, ταξινομημένη, μαζί με τις αντίστοιχες συχνότητες.
Εύκολο; Γιατί δεν δοκιμάζεις να το κάνεις; Πόσο εκτιμάς να σου πάρει; Θα περιμένω λίγο.
Το πρόβλημα δόθηκε στον διάσημο Donald Knuth (και αν δεν ξέρεις ποιος είναι αυτός ο κύριος σταμάτα, μάθε πρώτα και μετά συνέχισε την ανάγνωση) το 1986 για την στήλη “Programming Pearls” του μηνιαίου περιοδικού Communications of the ACM.
Η λύση του Knuth γραμμένη σε Pascal ήταν 10 σελίδες. Τα περισσότερα ήταν βέβαια σχόλια (και επειδή διάβασες και έμαθες για το τι ήταν ο Knuth θα έπρεπε να το περιμένεις αυτό). Η υλοποίηση του Knuth είχε μέσα μια όμορφη δομή δεδομένων για να κρατάει τις συχνότητες.
Σε απάντηση ο Doug McIlroy (που κάτι ήξερε από πίπες μιας και αυτός τις ανακάλυψε) έγραψε το παρακάτω
tr -cs A-Za-z '\n' |
tr A-Z a-z |
sort |
uniq -c |
sort -rn |
sed ${1}q
Αφήνοντας κατά μέρος το ζήτημα της απόδοσης, τι θα προτιμούσες να καταλάβεις; Τις 10 σελίδες του Knuth ή τα ιερογλυφικά του Doug McIlroy και τις πίπες του;
Πίπα (pipe) που την κάνουμε στο τερματικό με το σύμβολο | είναι ένας τρόπος να σπάμε ένα πρόβλημα σε μικρά προγράμματα που κάνουν μια μικρή αλλά συγκεκριμένη δουλεία και να τα συνδέουμε μεταξύ τους. Ελπίζω να μην σκεφτήκατε ότι θα μίλαγα για κάτι άλλο. Δεν είμαι clickbait τύπος.
Σίγουρα όλοι θα προτιμούσαμε την λύση του Doug McIlroy. Απλά βήματα που μπορούμε να καταλάβουμε τι κάνει το κάθε ένα (εντάξει δεν μπορούμε αν δεν είμαστε μάγοι στο bash, αλλά αν είμασταν;) . Δεν μας απασχολεί πως θα βρει ο υπολογιστής την απάντηση και τι βήματα θα κάνει. Εμείς θα καθήσουμε να του πούμε τι θέλουμε και να κάτσει να τα βρει μόνος του ο παλιοντεμπέλης. Ορίστε μας/
Το στυλ αυτό του προγραμματισμού είναι αυτό που οι επιστήμονες της πληροφορικής το λένε συναρτησιακός προγραμματισμός. Εντάξει αυτός περιέχει και άλλα πράγματα (βασικά μια γλώσσα είναι συναρτησιακή, μόνο αν έχει όλα τα χαρακτηριστικά της δικιάς σου αγαπημένης γλώσσας) αλλά ας μείνουμε σε αυτό για την ώρα.
Εντάξει τώρα, καλό το clickbait αλλά που κολλάει η C++; Πριν επιτρέψτε μου να μοιραστώ μια προσωπική στιγμή
std::vector<int>
count_lines_in_files(const std::vector<std::string>& files)
{
return files | transform(count_lines);
}
Δεν έχω ξανακλάψει ποτέ αντικρίζοντας μια τέτοια ομορφιά σε κώδικά. Αλήθεια λέω και είμαι μεγάλο παιδί.
Η κλασσική υλοποίηση σε C++ ή Java θα ήταν 20 γραμμές τουλάχιστον, που θα έπρεπε να τις διαβάσεις για να καταλάβεις τι κάνει, θα είχε ένα βρόχο και θα έπρεπε να τις δεις προσεχτικά γιατί κάθε γραμμή, μπορεί να περιέχει ένα λάθος. Και η απόδοση που θα σέρνετε θα πει κάποιος και θα κάνει λάθος, γιατί θα έχουν την ίδια.
Ο κώδικάς αυτός θα τρέξει σε κάθε μοντέρνο μεταγλωττιστή της C++. Σήμερα θέλει μια εξωτερική βιβλιοθήκη (ranges την λένε) αλλά αυτή θα ενσωματωθεί στην επόμενη C++20.
Οι επιστήμονες της πληροφορικής περίμεναν δεκαετίες για να γίνει δημοφηλής μια συναρτησιακή γλώσσα στην πιάτσα. Και τσάμπα περίμεναν, γιατί όλες οι καινούργιες γλώσσες έχουν κλέψει πράγματα από τις συναρτησιακές.
Ναι αλλά η C++ είναι μια παλιά γλώσσα θα πεις. Και θα έχεις άδικο, γιατί η C++11 είναι ουσιαστικά μια καινούργια γλώσσα. Και σταμάτα σε παρακαλώ να την λες αντικειμενοστραφή γιατί δεν είναι, η τουλάχιστον δεν είναι μόνο. Η C++ έχει πολλά πρόσωπα και η κεντρική της βιβλιοθήκη η STL δεν ακολουθεί καν το αντικειμενοστραφές παράδειγμα. Πλέον έχει μια ακόμα όψη.
Πίσω στα σχολεία λοιπόν. Και αυτό είναι το ωραίο στον προγραμματισμό. Πλέον με τους επεξεργαστές με τους πολους πυρήνες, τις ιεραρχίες τις cache και την gpgpu θέλουμε νέες γλώσσες, καθώς και να αλλάξουμε τις παλιές. Ξαναδές την αγαπημένη σου γλώσσα λοιπόν, δες τι καινούργια έχει να σου δώσει και πρόσθεσε και νέα εργαλεία στο οπλοστάσιο σου. Το ποια γλώσσα αυτή θα είναι είναι, αδιάφορο στην τελική.
Και οποίος θέλει ας δώσει την λύση του με όποιο τρόπο ή γλώσσα θέλει στο παλιό αυτό πρόβλημα από το μακρινό 1986.
Ο κώδικάς και η ιστορία είναι από το βιβλίο του Ivan Čukić “Functional Programming in C++” (Functional Programming in C++)