Tracks
/
Elixir
Elixir
/
Übungen
/
Malen nach Zahlen
Malen nach Zahlen

Malen nach Zahlen

Lernübung

Einführung

Bitstrings

Die Arbeit mit binären Daten ist in jeder Sprache ein wichtiges Konzept, und Elixir bietet eine elegante Syntax, um binäre Daten zu schreiben, abzugleichen und aufzubauen.

In Elixir werden binäre Daten als Bitstring-Typ bezeichnet. Der binäre Daten_typ_ (nicht zu verwechseln mit binären Daten im Allgemeinen) ist eine spezielle Form eines Bitstrings, die wir in einer späteren Übung besprechen.

Bitstring-Literale werden mit der speziellen Bitstring-Form <<>> definiert. Wenn du ein Bitstring-Literal definierst, wird es in Segmenten definiert. Jedes Segment hat einen Wert und einen Typ, die durch den Operator :: getrennt sind. Der Typ gibt an, wie viele Bits zur Kodierung des Werts verwendet werden. Der Typ kann ganz weggelassen werden, dann wird standardmäßig ein 8-Bit-Ganzzahlwert verwendet.

# This defines a bitstring with three segments of a single bit each
<<0::1, 1::1, 0::1>>

Die Angabe des Typs als ::1 ist eine Kurzschreibweise für ::size(1). Du musst die längere Syntax verwenden, wenn die Bitgröße aus einer Variable stammt.

Binär

Beim Schreiben binärer Ganzzahlliterale können wir sie direkt in Basis-2-Notation schreiben, indem wir dem Literal 0b voranstellen. Beachte, dass sie ohnehin als Dezimalzahlen angezeigt werden, wenn sie in Testergebnissen ausgegeben werden oder wenn du iex verwendest.

<<0b1011::4>> == <<11::4>>
# => true

Abschneiden

Wenn der Wert des Segments die Kapazität des Segmenttyps überschreitet, wird er von links abgeschnitten.

<<0b1011::3>> == <<0b0011::3>>
# => true

Voranstellen und Anhängen

Du kannst mit der speziellen Form sowohl an einen bestehenden Bitstring voranstellen als auch anhängen. Der Typ ::bitstring muss auf den bestehenden Bitstring angewendet werden, wenn dieser eine unbekannte Größe hat.

value = <<0b110::3, 0b001::3>>
new_value = <<0b011::3, value::bitstring, 0b000::3>>
# => <<120, 8::size(4)>>

Verketten

Wir können in Variablen gespeicherte Bitstrings mit der speziellen Form verketten. Der Typ ::bitstring muss verwendet werden, wenn zwei Bitstrings unbekannter Größe verkettet werden.

first = <<0b110::3>>
second = <<0b001::3>>
concatenated = <<first::bitstring, second::bitstring>>
# => <<49::size(6)>>

Musterabgleich

Mit der speziellen Form kann auch ein Musterabgleich durchgeführt werden, um Werte zu erhalten. Du musst die Anzahl der Bits für jedes Fragment kennen, das du erfassen möchtest, mit einer Ausnahme: Der Typ ::bitstring kann verwendet werden, um einen Musterabgleich auf einem Bitstring unbekannter Größe durchzuführen, aber das ist nur für das letzte Fragment möglich.

<<value::4, rest::bitstring>> = <<0b01101001::8>>
value == 0b0110
# => true

Bitstrings untersuchen

Note

Bitstrings werden (vom Test Runner oder in iex) möglicherweise in einem anderen Format ausgegeben als dem Format, mit dem sie erstellt wurden. Das führt beim Lernen von Bitstrings oft zu Verwirrung.

Standardmäßig werden Bitstrings in Fragmenten von 8 Bits (einem Byte) angezeigt, selbst wenn du sie mit Fragmenten einer anderen Größe erstellt hast.

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

Wenn du einen Bitstring erstellst, der einen druckbaren UTF-8-kodierten String darstellt, wird er als String angezeigt.

<<>>
# => ""

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

Anleitung

Malen nach Zahlen (auch bekannt als Color by Number) sind schwarz-weiße Bilder zum Ausmalen. Die verschiedenen Bereiche des Bildes sind mit unterschiedlichen Zahlen versehen, und die Zahlen entsprechen bestimmten Farben aus einer vorgegebenen Farbpalette. Das Ziel ist, die Bereiche mit den richtigen Farben auszufüllen, sodass am Ende ein wunderschönes buntes Bild entsteht. Es ist eine entspannende Beschäftigung für Kinder und Erwachsene gleichermaßen.

Du hast die Aufgabe, eine Malen-nach-Zahlen-App in Elixir zu schreiben. Deine App soll Bilder in einem eigenen Datenformat importieren und exportieren können. Du hast dich entschieden, Binärdateien zu verwenden, um deine Bilddaten zu speichern.

Note

Diese Übung setzt voraus, dass du mit Binärzahlen vertraut bist und die Grundlagen dahinter verstehst, wie man Binärzahlen in Dezimalzahlen und Dezimalzahlen in Binärzahlen umwandelt.

Stellen wir uns vor, du hast ein Bild von einem Smiley, wie das unten gezeigte. Das Bild hat einen weißen Hintergrund. Der Smiley hat einen schwarzen Rand und eine gelbe Füllfarbe.

Dieses Bild verwendet 3 Farben. Sagen wir, wir weisen diesen Farben Indizes zu:

  • 0 (binär: 0b00) für Weiß,
  • 1 (binär: 0b01) für Schwarz,
  • 2 (binär: 0b10) für Gelb.

Mithilfe dieser Farbindizes können wir jetzt die Farbe jedes Pixels darstellen.

Smiley Smiley mit Farbindizes

1. Die Bitgröße der Palette berechnen

Implementiere die Funktion PaintByNumber.palette_bit_size/1. Sie soll die Anzahl der Farben in der Palette entgegennehmen und zurückgeben, wie viele Bits nötig sind, um so viele Farbindizes als Binärzahlen darzustellen. Farbindizes beginnen immer bei 0 und sind fortlaufende, aufsteigende Ganzzahlen.

Zum Beispiel benötigen 13 verschiedene Farben 4 Bits. 4 Bits können bis zu 16 Farbindizes speichern (2^4). 3 Bits würden nicht ausreichen, denn 3 Bits können nur bis zu 8 Farbindizes speichern (2^3).

PaintByNumber.palette_bit_size(13)
# => 4

Hinweis: In der Elixir-Standardbibliothek gibt es keine log2-Funktion. Später lernst du, wie du aus Elixir heraus Erlang-Bibliotheken verwendest, in denen du diese Funktion findest. Löse diese Aufgabe jetzt stattdessen mit Rekursion und dem Potenzoperator **.

2. Ein leeres Bild erstellen

Implementiere die Funktion PaintByNumber.empty_picture/0. Sie soll einen leeren Bitstring zurückgeben.

3. Ein Testbild erstellen

Ein vordefiniertes Testbild wird verwendet, um die grundlegenden Funktionen deiner App manuell zu testen. Das Testbild besteht aus 4 Pixeln mit 4 verschiedenen Farben.

Implementiere die Funktion PaintByNumber.test_picture/0. Sie soll einen Bitstring zurückgeben, der aus 4 Segmenten besteht. Jedes Segment soll eine Bitbreite von 2 haben. Die Segmente sollen die Werte 0, 1, 2 und 3 haben.

4. Ein Pixel an den Anfang eines Bildes anhängen

Implementiere die Funktion PaintByNumber.prepend_pixel/3. Sie soll drei Argumente entgegennehmen: einen Bitstring mit dem Bild, an das wir vorne anhängen, die Anzahl der Farben in der Palette und den Index der Farbe für das neue Pixel. Sie soll einen Bitstring mit einem Bild zurückgeben, bei dem das neue Pixel am Anfang hinzugefügt wurde.

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. Das erste Pixel eines Bildes auslesen

Implementiere die Funktion PaintByNumber.get_first_pixel/2. Sie soll zwei Argumente entgegennehmen: einen Bitstring mit dem Bild, aus dem wir lesen, und die Anzahl der Farben in der Palette. Sie soll den Farbindex des ersten Pixels im gegebenen Bild zurückgeben. Wenn sie ein leeres Bild erhält, soll sie nil zurückgeben.

picture = <<19::5, 2::5, 18::5>>
color_count = 20

PaintByNumber.get_first_pixel(picture, color_count)
# => 19

6. Das erste Pixel eines Bildes entfernen

Implementiere die Funktion PaintByNumber.drop_first_pixel/2. Sie soll zwei Argumente entgegennehmen: einen Bitstring mit dem Bild, aus dem wir ein Pixel entfernen, und die Anzahl der Farben in der Palette. Sie soll das Bild ohne das erste Pixel zurückgeben. Wenn sie ein leeres Bild erhält, soll sie ein leeres Bild zurückgeben.

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. Zwei Bilder verketten

Implementiere die Funktion PaintByNumber.concat_pictures/2. Sie soll zwei Argumente entgegennehmen, zwei Bitstrings. Sie soll einen Bitstring zurückgeben, der entsteht, wenn man das erste Argument vor das zweite Argument hängt.

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>>)
Über GitHub bearbeiten Der Link öffnet sich in einem neuen Fenster oder Tab
Elixir Exercism

Bereit, mit Malen nach Zahlen zu starten?

Melde dich bei Exercism an, um Elixir mit 58 Konzepte168 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.