Το προφίλ μας στο Google Plus
1

Brainf*ck [μέρος 1ο]

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

Γλώσσες προγραμματισμού υπάρχουν δεκάδες, για κάθε γούστο και για κάθε σκοπό. Πέρα από τις επιχειρηματικές σκοπιμότητες, ο κύριος λόγος για την ανάπτυξη τόσων γλωσσών είναι η ποικιλία των προγραμματιστικών προβλημάτων και η προσπάθεια να διευκολυνθεί η δουλειά των προγραμματιστών. Κατά καιρούς έχουμε μελετήσει διάφορες γλώσσες, όπως είναι η Python, η PHP, η C# και φυσικά η C. Κάθε γλώσσα έχει τη δική της λογική, που βρίσκεται περισσότερο ή λιγότερο κοντά στη λογική των υπολογιστών. Σε κάθε περίπτωση, όμως, λίγο οι σελίδες του περιοδικού, λίγο η αγάπη μας για τους υπολογιστές και η διάθεση για πειραματισμούς, έχουμε καταφέρει κάτι που για ορισμένους φαντάζει αδιανόητο: Να νιώθουμε άνετα όταν βλέπουμε κώδικα και να εντοπίζουμε κοινά στοιχεία σε όλες τις γλώσσες προγραμματισμού. Αυτό συμβαίνει με σχεδόν όλες τις γλώσσες που λύνουν κάποιο πρόβλημα — ή, τέλος πάντων, έχουν μια κάποια χρησιμότητα. Νομίζετε ότι τώρα περιγράψαμε όλες τις γλώσσες;

Brainfuck, γιατί έτσι
Τι συμβαίνει όταν ο σκοπός μιας γλώσσας δε σχετίζεται με την επίλυση κάποιου προβλήματος κι εστιάζει αποκλειστικά στην επίδειξη προγραμματιστικών δυνατοτήτων; Κάποιοι θα πουν ότι αυτές οι γλώσσες είναι εντελώς άχρηστες και δεν αξίζει να σπαταλήσουν ούτε λεπτό για την εκμάθησή τους. Εμείς από τη μεριά μας θεωρούμε ότι είναι συναρπαστικές, κι όταν ξεκλέβουμε λίγο χρόνο προσπαθούμε να τις μαθαίνουμε παίζοντας. Μια τέτοια γλώσσα προγραμματισμού είναι και η Brainfuck, που αποτελεί μάλιστα την πιο διάσημη από τις λεγόμενες “εσωτερικές γλώσσες” (esoteric languages). Η Brainfuck αναπτύχθηκε το 1993 από τον Urban Müller, με μοναδικό σκοπό να αποτελέσει τη γλώσσα με τον μικρότερο compiler που μπορεί να υπάρξει. Περιττό να πούμε ότι αυτός ο στόχος έχει επιτευχθεί, αφού ορισμένοι compilers έχουν φτάσει στο εντυπωσιακά μικροσκοπικό μέγεθος των 100 bytes. Η δομή της Brainfuck είναι βασισμένη σε αυτήν της γλώσσας P′′ κι έχει εμπλουτιστεί με δύο μόνο εντολές, για την είσοδο και την έξοδο δεδομένων.
Πιο Λακωνικός κι από Σπαρτιάτης
Η Brainfuck διαθέτει μόλις οκτώ εντολές, καθεμία από τις οποίες συμβολίζεται με έναν χαρακτήρα. Στην ακόλουθη γραμμή φαίνονται όλες οι εντολές της:

+ - < > [ ] , .

Το μοντέλο λειτουργίας της Brainfuck στηρίζεται σε ένα byte array, με μέγεθος τουλάχιστον 30000 κελιών. Το σύνολο των κελιών του πίνακα αρχικοποιείται με την τιμή μηδέν (0). Υπάρχει ένας δείκτης δεδομένων (data pointer) με τον οποίο μπορούμε να κινούμαστε μεταξύ των κελιών του πίνακα, ενώ κάθε φορά μπορούμε να αυξάνουμε ή να μειώνουμε την τιμή του επιλεγμένου κελιού. Κατά την εκκίνηση κάθε προγράμματος, ο δείκτης παραπέμπει στο πρώτο κελί του πίνακα (μας αρέσει να το φανταζόμαστε τέρμα αριστερά ;)) Τέλος, υπάρχουν δύο byte streams: Ένα για την είσοδο από το πληκτρολόγιο κι ένα για την έξοδο προς την οθόνη. Τα προγράμματα αλληλεπιδρούν με το χρήστη μέσω αυτών των δύο ροών. Ας δούμε τώρα και την ερμηνεία των εντολών.

Όλες οι εντολές της Brainfuck και η ερμηνεία τους.

Τα προγράμματα στη Brainfuck συγκροτούνται αποκλειστικά από τους παραπάνω οκτώ χαρακτήρες – εντολές. Οποιοσδήποτε άλλος χαρακτήρας αγνοείται από τον compiler της γλώσσας και, ουσιαστικά, αντιμετωπίζεται σαν σχόλιο. Έτσι, μπορούμε να προσθέτουμε σχόλια πανεύκολα, κάτι που στην περίπτωση της Brainfuck είναι περισσότερο χρήσιμο από κάθε άλλη γλώσσα ;)

Το πρώτο μας πρόγραμμα
Επειδή η Brainfuck δεν αποτελεί μια τυπική γλώσσα προγραμματισμού, δεν θα ξεκινήσουμε με το κλασικό “hello, world!”. Θα ξεκινήσουμε με κάτι πιο απλό: Μια απλή εκδοχή του προγράμματος echo, το οποίο θα εμφανίζει στην οθόνη κάθε χαρακτήρα που δίνουμε, μέχρι να εισαχθεί ο χαρακτήρας NULL. Ορίστε ο κώδικας του προγράμματος:

,[.,]

Όχι, δεν πρόκειται για τυπογραφικό λάθος. Ολόκληρο το πρόγραμμα που περιγράψαμε απαρτίζεται από αυτούς τους πέντε χαρακτήρες. Ας αναλύσουμε τον κώδικα χαρακτήρα προς χαρακτήρα και όχι γραμμή προς γραμμή, όπως κάνουμε συνήθως. Ο πρώτος χαρακτήρας διαβάζει έναν χαρακτήρα από το input stream και αποθηκεύει τον αντίστοιχο κωδικό ASCII στο κελί που παραπέμπει ο δείκτης. Εφόσον βρισκόμαστε στο ξεκίνημα του προγράμματος και δεν έχουμε τροποποιήσει τον δείκτη, ο κωδικός ASCII θα καταλήξει στο πρώτο κελί του πίνακα. Έτσι, αν για παράδειγμα πατήσουμε το κουμπί Α, στο πρώτο κελί του πίνακα θα αποθηκευτεί ο αριθμός 65. Το πρόγραμμα συνεχίζει με το χαρακτήρα της αριστερής αγκύλης, που σηματοδοτεί την έναρξη ενός βρόχου. Εφόσον στο τρέχον κελί έχει αποθηκευτεί το 65 και, τέλος πάντων, κάτι άλλο εκτός από μηδέν, θα ξεκινήσει η εκτέλεση του περιεχομένου του βρόχου. Μέσα στο βρόχο συναντάμε το χαρακτήρας της τελείας, που μεταφέρει στο output stream την τιμή του τρέχοντος κελιού. Έτσι, ο χαρακτήρας με κωδικό ASCII το 65 (δηλαδή το A) θα εμφανιστεί στην οθόνη. Στη συνέχεια συναντούμε πάλι την εντολή εισόδου (,). Εφόσον δεν έχουμε μετατοπίσει καθόλου το δείκτη, ο κωδικός ASCII του νέου χαρακτήρα που δίνουμε θα αποθηκευτεί στην ίδια θέση και θα αντικαταστήσει την τιμή 65. Τέλος συναντάμε το χαρακτήρα της δεξιάς αγκύλης, που δηλώνει τον τερματισμό του βρόχου. Αν η αποθηκευμένη τιμή στο τρέχον κελί είναι διάφορη του μηδενός, η ροή του προγράμματος θα μεταφερθεί στην αμέσως προηγούμενη αριστερή αγκύλη και άρα στο ξεκίνημα του βρόχου. Έτσι, ο βρόχος που περιγράψαμε συνεχίζει να εκτελείται έως ότου εισάγουμε το χαρακτήρα NULL, ο οποίος έχει σαν κωδικό ASCII το 0. Μαζί με το βρόχο θα τερματιστεί και το πρόγραμμα, αφού δεν υπάρχουν άλλες εντολές.

Σ’ αυτό το σημείο, όσοι ξεπεράσουν τη ζαλάδα που προκαλεί ο κώδικας σε Brainfuck, ενδέχεται να διαμαρτυρηθούν: Πώς είναι δυνατό να εισάγουμε από το πληκτρολόγιο το χαρακτήρα NULL; Η αλήθεια είναι ότι κάτι τέτοιο δεν είναι εφικτό. Έτσι, αποφασίσαμε να δημιουργήσουμε μια παραλλαγή του προγράμματος, στην οποία ο βρόχος θα τερματίζεται όταν εισάγουμε τον χαρακτήρα του κενού (κωδικός ASCII 32). Αναρωτιέστε πώς θα πραγματοποιήσουμε αυτόν τον έλεγχο; Όπως έχουμε πει, ο χαρακτήρας τερματισμού των βρόχων κάνει τη δουλειά του (δηλαδή τερματίζει τον εκάστοτε βρόχο), μόνον όταν η τιμή του τρέχοντος κελιού είναι 0. Ε, λοιπόν, αφού έτσι έχουν τα πράγματα, θα πραγματοποιούμε τον έλεγχο αφού προηγουμένως αφαιρέσουμε 32 μονάδες από την τιμή του τρέχοντος κελιού! Έτσι, η συγκεκριμένη τιμή θα μηδενίζεται μόνον όταν έχουμε δώσει το χαρακτήρα του κενού, ο οποίος έχει κωδικό ASCII το 32. Τώρα χρειάζεται λίγη προσοχή: Στην περίπτωση που ο βρόχος δεν πρέπει να τερματιστεί, ο χαρακτήρας που δώσαμε πρέπει να εμφανιστεί στην οθόνη. Ακριβώς γι’ αυτό, πρέπει να προσθέσουμε τις 32 μονάδες που αφαιρέσαμε προηγουμένως, ώστε να σχηματιστεί και πάλι ο κωδικός ASCII. Απλό δεν είναι; Δείτε και την ανανεωμένη μορφή του κώδικα:

,--------------------------------[++++++++++++++++++++++++++++++++.,
--------------------------------]

Για μεγαλύτερη ευκολία στην ανάγνωση, αν και κάτι τέτοιο αποτελεί αστείο στην περίπτωση της Brainfuck, το πρόγραμμά μας θα μπορούσε να γραφτεί και έτσι:

,
--------------------------------
[
    ++++++++++++++++++++++++++++++++
    . ,
    --------------------------------
]

Η αφαίρεση 32 μονάδων από την τρέχουσα τιμή του κελιού που βρισκόμαστε, επιτυγχάνεται με την εκτέλεση της εντολής μείωσης (-) 32 φορές. Αντίστοιχα, η αύξηση κατά 32 μονάδες πραγματοποιείται με την εκτέλεση της εντολής αύξησης (+) 32 φορές.

Αν και το παράδειγμά μας είναι πολύ απλό, προσφέρει μια καλή γεύση του προγραμματισμού σε Brainfuck. Ωστόσο, δεν είδαμε καθόλου τις εντολές μετακίνησης του δείκτη. Δε νομίζετε ότι πρέπει να κάνουμε κάτι γι’ αυτό; Εμείς πάντως το νομίζουμε — και γι’ αυτό θα παρουσιάσουμε ένα ακόμα πρόγραμμα: Το κλασικό “Hello World!”

Αντίο κόσμε
Όπως είδαμε, η εντολή της τελείας εμφανίζει στην οθόνη το χαρακτήρα που βρίσκεται αποθηκευμένος στο τρέχον κελί. Επιπρόσθετα, γνωρίζουμε ότι στο ξεκίνημα ενός προγράμματος όλα τα κελιά παίρνουν την τιμή μηδέν. Επομένως, για να εμφανίσουμε ένα συγκεκριμένο χαρακτήρα στην οθόνη, πρέπει πρώτα να προσθέσουμε τόσες μονάδες στο τρέχον κελί, όσες απαιτούνται για να σχηματιστεί ο επιθυμητός κωδικός ASCII. Στη συνέχεια αρκεί να εκτελέσουμε την εντολή της τελείας. Για παράδειγμα, δείτε τον κώδικα που απαιτείται για την εκτύπωση του χαρακτήρα “Η” (κωδικός ASCCI 72). Υποθέτουμε ότι το πρόγραμμά μας μόλις ξεκίνησε και το τρέχον κελί έχει την τιμή μηδέν. Έτσι, προσθέτουμε 72 μονάδες και εκτελούμε την εντολή της τελείας:

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.

Η παραπάνω λύση, όταν θέλουμε να τυπώσουμε έναν χαρακτήρα και μόνο, είναι υποφερτή. Όταν όμως σκοπεύουμε να τυπώσουμε ένα ολόκληρο μήνυμα, η συγκεκριμένη προσέγγιση γίνεται φρικτή. Για να επιβιώσουμε στον κόσμο της Brainfuck, πρέπει να επιστρατεύσουμε και την τελευταία σταγόνα ευρηματικότητας. Μια καλύτερη λύση, λοιπόν, είναι να δημιουργήσουμε ένα βρόχο που προσθέτει δέκα μονάδες, να τον εκτελέσουμε 7 φορές και στη συνέχεια να προσθέσουμε άλλες δύο μονάδες. Δείτε τον κώδικα:

+++++++
[-&gt;++++++++++&lt;]
>++.

Αρχικά αποθηκεύουμε την τιμή επτά, στο τρέχον κελί. Όπως αντιλαμβάνεστε, η τιμή του τρέχοντος κελιού θα παίξει το ρόλο του μετρητή και ο βρόχος θα επαναλαμβάνεται έως ότου αυτός ο μετρητής μηδενιστεί. Ο βρόχος ξεκινά με τη μείωση της τιμής του τρέχοντος κελιού. Στη συνέχεια μετατοπιζόμαστε στο επόμενο κελί, προσθέτουμε δέκα μονάδες και αμέσως μετά επιστρέφουμε στο προηγούμενο κελί. Εφόσον η τιμή αυτού του κελιού (δηλαδή του μετρητή) δεν έχει μηδενιστεί, επαναλαμβάνεται ο κώδικας του βρόχου. Με αυτό τον τρόπο, ο κώδικας που προσθέτει δέκα μονάδες στο επόμενο κελί, θα εκτελεστεί επτά φορές. Χρησιμοποιώντας πιο προσφιλείς όρους, θα μπορούσαμε να πούμε ότι το κελί 0 χρησιμοποιείται σαν μια προσωρινή μεταβλητή, ενώ το κελί 1 αποτελεί τη μεταβλητή στην οποία θέλουμε να προσθέσουμε τις επτά δεκάδες. Όταν ολοκληρωθούν οι επαναλήψεις του βρόχου, μετατοπιζόμαστε πάλι στο επόμενο κελί, προσθέτουμε δύο ακόμα μονάδες και μετά εκτελούμε την εντολή της εκτύπωσης.

Μπορεί το κολπάκι με το βρόχο να σας φάνηκε σύνθετο, αλλά περιορίζει σημαντικά την πιθανότητα να κάνουμε κάποιο λάθος στο μέτρημα των χαρακτήρων και, τελικά, στο πλήθος των μονάδων που θα προστεθούν σε ένα κελί. Με τη βοήθεια αυτού του μικρού τεχνάσματος θα φτιάξουμε και το “Hello World!”. Εξετάζοντας τους κωδικούς ASCII των γραμμάτων που σχηματίζουν αυτό το μήνυμα, διαπιστώνουμε ότι θα βόλευε να έχουμε εύκαιρους τους αριθμούς 70, 100, 30 και 90. Έτσι, θα φροντίσουμε να τους σχηματίσουμε σε τέσσερα διαδοχικά κελιά της μνήμης…

++++++++++  Κελί 0: αποθηκεύουμε την τιμή 10
[-          Μείωση του κελιού 0 κατά 1 μονάδα
>+++++++    Κελί 1: προσθέτουμε 7 μονάδες
>++++++++++ Κελί 2: προσθέτουμε 10 μονάδες
>+++        Κελί 3: προσθέτουμε 3 μονάδες
>+++++++++  Κελί 4: προσθέτουμε 9 μονάδες
&lt;&lt;&lt;&lt;]       Επιστρέφουμε στο κελί 0

>++         Κελί 1: Προσθέτουμε δύο μονάδες (10 * 7 + 2 = 72)
.           Εμφάνιση χαρακτήρα ASCII 72 (Η)
>+          Κελί 2: Αύξηση κατά μία μονάδα: (10 * 10 + 1 = 101)
.           Εμφάνιση του χαρακτήρα 101 (e)
+++++++     Πρόσθεση επτά μονάδων (101 + 7 = 108)
.           Εμφάνιση του χαρακτήρα 108 (l)
.           Εμφάνιση του χαρακτήρα 108 (l)
+++         Πρόσθεση τριών μονάδων (108 + 3 = 111)
.           Εμφάνιση του χαρακτήρα 111 (o)
>++         Κελί 3: Αύξηση κατά δύο μονάδες (10 * 3 + 2 = 32)
.           Εμφάνιση του χαρακτήρα 32 ( )
>---        Κελί 4: Μείωση κατά 3 μονάδες (10 * 9 – 4 = 87)
.           Εμφάνιση του χαρακτήρα 87  (W)
&lt;&lt;          Μετακίνηση στο κελί 2 (111)
.           Εμφάνιση του χαρακτήρα 111 (o)
+++         Κελί 2: Αύξηση κατά τρεις μονάδες (111 + 3 = 114)
.           Εμφάνιση του χαρακτήρα 114 (r)
------      Κελί 2: Μείωση κατά έξι μονάδες (114 – 6 = 108)
.           Εμφάνιση του χαρακτήρα 108 (l)
--------    Κελί 2: Μείωση κατά οκτώ μονάδες (108 – 8 = 100)
.           Εμφάνιση του χαρακτήρα 100 (d)
>+          Κελί 3: Αύξηση κατά μία μονάδα (32 + 1 = 33)
.           Εμφάνιση του χαρακτήρα 33 (!)

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

++++++++++[->+++++++>++++++++++>+++>+++++++++<<<<]>++
.>+.+++++++..+++.>++.>---.<<.+++.------.--------.>+.

Τι έχετε να πείτε τώρα; Ελπίζουμε αυτή η τελευταία παράθεση κώδικα να σχημάτισε και στο δικό σας πρόσωπο ένα χαμόγελο! Σ’ εμάς πάντως τα κατάφερε και, όταν ξεπεράσαμε αυτή την αρχική χαρά (και την παραζάλη), αποφασίσαμε να ασχοληθούμε περισσότερο με τη Brainfuck. Αν σας ανοίξαμε την όρεξη, προτείνουμε να συνεχίσετε τη γνωριμία με αυτή την απλοϊκή αλλά συναρπαστικά περίπλοκη γλώσσα.

One Response to “Brainf*ck [μέρος 1ο]”

  1. psychaos | 26/04/2015 at 05:23

    Απίστευτο…!! Πολύ ενδιαφέρον άρθρο!

Leave a Reply

You must be logged in to post a comment.

Σύνδεση

Αρχείο δημοσιεύσεων