Η εργασία με δυαδικά δεδομένα είναι μια σημαντική έννοια σε κάθε γλώσσα προγραμματισμού, και η Elixir παρέχει μια κομψή σύνταξη για να γράφεις, να ταιριάζεις και να κατασκευάζεις δυαδικά δεδομένα.
Στην Elixir, τα δυαδικά δεδομένα αναφέρονται ως τύπος bitstring. Ο τύπος δυαδικών δεδομένων (για να μην τον συγχέουμε με τα δυαδικά δεδομένα γενικά) είναι μια ειδική μορφή bitstring, την οποία θα συζητήσουμε σε επόμενη άσκηση.
Τα literals bitstring ορίζονται χρησιμοποιώντας την ειδική μορφή bitstring <<>>. Όταν ορίζεις ένα literal bitstring, το ορίζεις σε τμήματα. Κάθε τμήμα έχει μια τιμή και έναν τύπο, που χωρίζονται με τον τελεστή ::. Ο τύπος καθορίζει πόσα bits θα χρησιμοποιηθούν για την κωδικοποίηση της τιμής. Ο τύπος μπορεί να παραλειφθεί εντελώς, οπότε προεπιλέγεται μια ακέραια τιμή 8 bits.
# This defines a bitstring with three segments of a single bit each
<<0::1, 1::1, 0::1>>
Ο καθορισμός του τύπου ως ::1 είναι συντομογραφία για το ::size(1). Πρέπει να χρησιμοποιήσεις τη μακρύτερη σύνταξη αν το μέγεθος σε bits προέρχεται από μια μεταβλητή.
Όταν γράφουμε ακέραια δυαδικά literals, μπορούμε να τα γράψουμε απευθείας σε συμβολισμό βάσης 2, βάζοντας το πρόθεμα 0b μπροστά από το literal. Σημείωσε ότι ούτως ή άλλως θα εμφανίζονται ως δεκαδικοί αριθμοί όταν τυπώνονται στα αποτελέσματα των test ή όταν χρησιμοποιείς το iex.
<<0b1011::4>> == <<11::4>>
# => true
Αν η τιμή του τμήματος υπερβεί τη χωρητικότητα του τύπου του τμήματος, θα αποκοπεί από τα αριστερά.
<<0b1011::3>> == <<0b0011::3>>
# => true
Μπορείς και να προσθέσεις στην αρχή και να προσθέσεις στο τέλος ενός υπάρχοντος bitstring χρησιμοποιώντας την ειδική μορφή. Ο τύπος ::bitstring πρέπει να χρησιμοποιείται στο υπάρχον bitstring αν αυτό έχει άγνωστο μέγεθος.
value = <<0b110::3, 0b001::3>>
new_value = <<0b011::3, value::bitstring, 0b000::3>>
# => <<120, 8::size(4)>>
Μπορούμε να συνενώσουμε bitstrings που είναι αποθηκευμένα σε μεταβλητές χρησιμοποιώντας την ειδική μορφή. Ο τύπος ::bitstring πρέπει να χρησιμοποιείται όταν συνενώνεις δύο bitstrings άγνωστου μεγέθους.
first = <<0b110::3>>
second = <<0b001::3>>
concatenated = <<first::bitstring, second::bitstring>>
# => <<49::size(6)>>
Μπορείς επίσης να κάνεις αντιστοίχιση προτύπων για να πάρεις τιμές από την ειδική μορφή. Πρέπει να ξέρεις τον αριθμό των bits για κάθε κομμάτι που θέλεις να συλλάβεις, με μία εξαίρεση: ο τύπος ::bitstring μπορεί να χρησιμοποιηθεί για αντιστοίχιση προτύπων σε ένα bitstring άγνωστου μεγέθους, αλλά αυτό μπορεί να χρησιμοποιηθεί μόνο για το τελευταίο κομμάτι.
<<value::4, rest::bitstring>> = <<0b01101001::8>>
value == 0b0110
# => true
Τα bitstrings μπορεί να τυπωθούν (από τον test runner ή στο iex) σε διαφορετική μορφή από τη μορφή που χρησιμοποιήθηκε για τη δημιουργία τους. Αυτό συχνά προκαλεί σύγχυση όταν μαθαίνεις bitstrings.
Από προεπιλογή, τα bitstrings εμφανίζονται σε κομμάτια των 8 bits (ένα byte), ακόμα κι αν τα δημιούργησες με κομμάτια διαφορετικού μεγέθους.
<<2011::11>>
# => <<251, 3::size(3)>>
Αν δημιουργήσεις ένα bitstring που αντιπροσωπεύει μια εκτυπώσιμη συμβολοσειρά κωδικοποιημένη σε UTF-8, αυτό εμφανίζεται ως συμβολοσειρά.
<<>>
# => ""
<<65, 66, 67>>
# => "ABC"
Ζωγραφική με αριθμούς (γνωστή και ως χρωματισμός με αριθμούς) είναι ασπρόμαυρες εικόνες προορισμένες για χρωματισμό. Οι διάφορες περιοχές της εικόνας σημειώνονται με διαφορετικούς αριθμούς, και οι αριθμοί αντιστοιχούν σε συγκεκριμένα χρώματα από μια προκαθορισμένη παλέτα χρωμάτων. Ο στόχος είναι να γεμίσεις τις περιοχές με τα σωστά χρώματα, αποκαλύπτοντας μια όμορφη πολύχρωμη εικόνα στο τέλος. Είναι μια χαλαρωτική δραστηριότητα τόσο για παιδιά όσο και για ενήλικες.
Σου έχει ανατεθεί να γράψεις μια εφαρμογή ζωγραφικής με αριθμούς σε Elixir. Θέλεις η εφαρμογή σου να μπορεί να εισάγει και να εξάγει εικόνες σε μια προσαρμοσμένη μορφή δεδομένων. Έχεις αποφασίσει να χρησιμοποιήσεις δυαδικά αρχεία για να αποθηκεύσεις τα δεδομένα της εικόνας σου.
Αυτή η άσκηση προϋποθέτει ότι είσαι εξοικειωμένος με δυαδικούς αριθμούς και κατανοείς τις αρχές πίσω από τη μετατροπή δυαδικών αριθμών σε δεκαδικούς και δεκαδικών σε δυαδικούς.
Ας φανταστούμε ότι έχεις μια εικόνα με ένα χαμογελαστό προσωπάκι, σαν αυτή που φαίνεται παρακάτω. Η εικόνα έχει λευκό φόντο. Το χαμογελαστό προσωπάκι έχει μαύρο περίγραμμα και κίτρινο χρώμα γεμίσματος.
Αυτή η εικόνα χρησιμοποιεί 3 χρώματα. Ας πούμε ότι αναθέτουμε θέσεις σε αυτά τα χρώματα:
0 (δυαδικό: 0b00) για το λευκό,1 (δυαδικό: 0b01) για το μαύρο,2 (δυαδικό: 0b10) για το κίτρινο.Μπορούμε τώρα να χρησιμοποιήσουμε αυτές τις θέσεις χρώματος για να αναπαραστήσουμε το χρώμα κάθε pixel.
| Χαμογελαστό προσωπάκι | Χαμογελαστό προσωπάκι με θέσεις χρωμάτων |
|---|---|
![]() |
![]() |
Υλοποίησε τη συνάρτηση PaintByNumber.palette_bit_size/1. Θα πρέπει να δέχεται το πλήθος των χρωμάτων στην παλέτα και να επιστρέφει πόσα bit χρειάζονται για να αναπαραστήσεις τόσες θέσεις χρωμάτων ως δυαδικούς αριθμούς. Οι θέσεις χρωμάτων ξεκινούν πάντα από το 0 και είναι συνεχόμενοι αύξοντες ακέραιοι.
Για παράδειγμα, η αναπαράσταση 13 διαφορετικών χρωμάτων απαιτεί 4 bit. Τα 4 bit μπορούν να αποθηκεύσουν έως 16 θέσεις χρωμάτων (2^4). Τα 3 bit δεν θα ήταν αρκετά, επειδή τα 3 bit μπορούν να αποθηκεύσουν μόνο έως 8 θέσεις χρωμάτων (2^3).
PaintByNumber.palette_bit_size(13)
# => 4
Σημείωση: δεν υπάρχει συνάρτηση log2 στην πρότυπη βιβλιοθήκη της Elixir. Αργότερα θα μάθεις πώς να χρησιμοποιείς βιβλιοθήκες Erlang από την Elixir, όπου μπορείς να βρεις αυτή τη συνάρτηση. Τώρα, λύσε αυτό το έργο με αναδρομή και τον τελεστή ύψωσης σε δύναμη ** αντ' αυτού.
Υλοποίησε τη συνάρτηση PaintByNumber.empty_picture/0. Θα πρέπει να επιστρέφει ένα bitstring.
Μια προκαθορισμένη εικόνα δοκιμής θα χρησιμοποιηθεί για χειροκίνητο έλεγχο των βασικών λειτουργιών της εφαρμογής σου. Η εικόνα δοκιμής αποτελείται από 4 pixel με 4 διαφορετικά χρώματα.
Υλοποίησε τη συνάρτηση PaintByNumber.test_picture/0. Θα πρέπει να επιστρέφει ένα bitstring που αποτελείται από 4 τμήματα. Κάθε τμήμα θα πρέπει να έχει μέγεθος bit 2. Τα τμήματα θα πρέπει να έχουν τιμές 0, 1, 2 και 3.
Υλοποίησε τη συνάρτηση PaintByNumber.prepend_pixel/3. Θα πρέπει να δέχεται τρία ορίσματα: ένα bitstring με την εικόνα στην οποία προσθέτουμε στην αρχή, το πλήθος των χρωμάτων στην παλέτα και τη θέση του χρώματος για το νέο pixel. Θα πρέπει να επιστρέφει ένα bitstring με μια εικόνα όπου το νέο pixel έχει προστεθεί στην αρχή.
picture = <<2::4, 0::4>>
color_count = 13
pixel_color_index = 11
PaintByNumber.prepend_pixel(picture, color_count, pixel_color_index)
# => <<178, 0::size(4)>>
# (which is equal to <<11::4, 2::4, 0::4>>)
Υλοποίησε τη συνάρτηση PaintByNumber.get_first_pixel/2. Θα πρέπει να δέχεται δύο ορίσματα: ένα bitstring με την εικόνα από την οποία διαβάζουμε και το πλήθος των χρωμάτων στην παλέτα. Θα πρέπει να επιστρέφει τη θέση χρώματος του πρώτου pixel στη δεδομένη εικόνα. Όταν της δίνεται μια κενή εικόνα, θα πρέπει να επιστρέφει nil.
picture = <<19::5, 2::5, 18::5>>
color_count = 20
PaintByNumber.get_first_pixel(picture, color_count)
# => 19
Υλοποίησε τη συνάρτηση PaintByNumber.drop_first_pixel/2. Θα πρέπει να δέχεται δύο ορίσματα: ένα bitstring με την εικόνα από την οποία αφαιρούμε ένα pixel και το πλήθος των χρωμάτων στην παλέτα. Θα πρέπει να επιστρέφει την εικόνα χωρίς το πρώτο pixel. Όταν της δίνεται μια κενή εικόνα, θα πρέπει να επιστρέφει μια κενή εικόνα.
picture = <<2::3, 5::3, 5::3, 0::3>>
color_count = 6
PaintByNumber.drop_first_pixel(picture, color_count)
# => <<180, 0::size(1)>>
# (which is equal to <<5::3, 5::3, 0::3>>)
Υλοποίησε τη συνάρτηση PaintByNumber.concat_pictures/2. Θα πρέπει να δέχεται δύο ορίσματα, δύο bitstrings. Θα πρέπει να επιστρέφει ένα bitstring που είναι το αποτέλεσμα της προσθήκης του πρώτου ορίσματος στην αρχή του δεύτερου ορίσματος.
picture1 = <<52::6, 51::6>>
picture2 = <<0::6, 34::6, 12::6>>
PaintByNumber.concat_pictures(picture1, picture2)
# => <<211, 48, 34, 12::size(6)>>
# (which is equal to <<52::6, 51::6, 0::6, 34::6, 12::6>>)
Γράψου στο Exercism για να μάθεις και να κατακτήσεις Elixir με 58 έννοιες168 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.