Διαδρομές
/
Elixir
Elixir
/
Ασκήσεις
/
Ζωγραφική με αριθμούς
Ζωγραφική με αριθμούς

Ζωγραφική με αριθμούς

Άσκηση εκμάθησης

Εισαγωγή

Bitstrings

Η εργασία με δυαδικά δεδομένα είναι μια σημαντική έννοια σε κάθε γλώσσα προγραμματισμού, και η 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

Note

Τα bitstrings μπορεί να τυπωθούν (από τον test runner ή στο iex) σε διαφορετική μορφή από τη μορφή που χρησιμοποιήθηκε για τη δημιουργία τους. Αυτό συχνά προκαλεί σύγχυση όταν μαθαίνεις bitstrings.

Από προεπιλογή, τα bitstrings εμφανίζονται σε κομμάτια των 8 bits (ένα byte), ακόμα κι αν τα δημιούργησες με κομμάτια διαφορετικού μεγέθους.

<<2011::11>>
# => <<251, 3::size(3)>>

Αν δημιουργήσεις ένα bitstring που αντιπροσωπεύει μια εκτυπώσιμη συμβολοσειρά κωδικοποιημένη σε UTF-8, αυτό εμφανίζεται ως συμβολοσειρά.

<<>>
# => ""

<<65, 66, 67>>
# => "ABC"

Οδηγίες

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

Σου έχει ανατεθεί να γράψεις μια εφαρμογή ζωγραφικής με αριθμούς σε Elixir. Θέλεις η εφαρμογή σου να μπορεί να εισάγει και να εξάγει εικόνες σε μια προσαρμοσμένη μορφή δεδομένων. Έχεις αποφασίσει να χρησιμοποιήσεις δυαδικά αρχεία για να αποθηκεύσεις τα δεδομένα της εικόνας σου.

Note

Αυτή η άσκηση προϋποθέτει ότι είσαι εξοικειωμένος με δυαδικούς αριθμούς και κατανοείς τις αρχές πίσω από τη μετατροπή δυαδικών αριθμών σε δεκαδικούς και δεκαδικών σε δυαδικούς.

Ας φανταστούμε ότι έχεις μια εικόνα με ένα χαμογελαστό προσωπάκι, σαν αυτή που φαίνεται παρακάτω. Η εικόνα έχει λευκό φόντο. Το χαμογελαστό προσωπάκι έχει μαύρο περίγραμμα και κίτρινο χρώμα γεμίσματος.

Αυτή η εικόνα χρησιμοποιεί 3 χρώματα. Ας πούμε ότι αναθέτουμε θέσεις σε αυτά τα χρώματα:

  • 0 (δυαδικό: 0b00) για το λευκό,
  • 1 (δυαδικό: 0b01) για το μαύρο,
  • 2 (δυαδικό: 0b10) για το κίτρινο.

Μπορούμε τώρα να χρησιμοποιήσουμε αυτές τις θέσεις χρώματος για να αναπαραστήσουμε το χρώμα κάθε pixel.

Χαμογελαστό προσωπάκι Χαμογελαστό προσωπάκι με θέσεις χρωμάτων

1. Υπολόγισε το μέγεθος bit της παλέτας

Υλοποίησε τη συνάρτηση 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, όπου μπορείς να βρεις αυτή τη συνάρτηση. Τώρα, λύσε αυτό το έργο με αναδρομή και τον τελεστή ύψωσης σε δύναμη ** αντ' αυτού.

2. Δημιούργησε μια κενή εικόνα

Υλοποίησε τη συνάρτηση PaintByNumber.empty_picture/0. Θα πρέπει να επιστρέφει ένα bitstring.

3. Δημιούργησε μια εικόνα δοκιμής

Μια προκαθορισμένη εικόνα δοκιμής θα χρησιμοποιηθεί για χειροκίνητο έλεγχο των βασικών λειτουργιών της εφαρμογής σου. Η εικόνα δοκιμής αποτελείται από 4 pixel με 4 διαφορετικά χρώματα.

Υλοποίησε τη συνάρτηση PaintByNumber.test_picture/0. Θα πρέπει να επιστρέφει ένα bitstring που αποτελείται από 4 τμήματα. Κάθε τμήμα θα πρέπει να έχει μέγεθος bit 2. Τα τμήματα θα πρέπει να έχουν τιμές 0, 1, 2 και 3.

4. Πρόσθεσε ένα pixel στην αρχή μιας εικόνας

Υλοποίησε τη συνάρτηση 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>>)

5. Πάρε το πρώτο pixel από μια εικόνα

Υλοποίησε τη συνάρτηση 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

6. Αφαίρεσε το πρώτο pixel από μια εικόνα

Υλοποίησε τη συνάρτηση 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>>)

7. Συνένωσε δύο εικόνες

Υλοποίησε τη συνάρτηση 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>>)
Επεξεργασία μέσω GitHub Ο σύνδεσμος ανοίγει σε νέο παράθυρο ή καρτέλα
Elixir Exercism

Έτοιμος να ξεκινήσεις την άσκηση Ζωγραφική με αριθμούς;

Γράψου στο Exercism για να μάθεις και να κατακτήσεις Elixir με 58 έννοιες168 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.