Rutas
/
Elixir
Elixir
/
Ejercicios
/
Pintar por números
Pintar por números

Pintar por números

Ejercicio de aprendizaje

Introducción

Bitstrings

Trabajar con datos binarios es un concepto importante en cualquier lenguaje, y Elixir proporciona una sintaxis elegante para escribir, hacer coincidir y construir datos binarios.

En Elixir, los datos binarios se conocen como el tipo bitstring. El tipo de datos binarios (que no debe confundirse con los datos binarios en general) es una forma específica de bitstring, que veremos en un ejercicio posterior.

Los literales de bitstring se definen utilizando la forma especial de bitstring <<>>. Al definir un literal de bitstring, este se define en segmentos. Cada segmento tiene un valor y un tipo, separados por el operador ::. El tipo especifica cuántos bits se utilizarán para codificar el valor. El tipo se puede omitir por completo, en cuyo caso se usará por defecto un valor entero de 8 bits.

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

Especificar el tipo como ::1 es una forma abreviada de escribir ::size(1). Tienes que usar la sintaxis más larga si el tamaño en bits proviene de una variable.

Binario

Al escribir literales de números enteros binarios, podemos escribirlos directamente en notación de base 2 anteponiendo 0b al literal. Ten en cuenta que, de todos modos, se mostrarán como números decimales al imprimirlos en los resultados de los tests o al usar iex.

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

Truncado

Si el valor del segmento desborda la capacidad del tipo del segmento, se truncará por la izquierda.

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

Añadir al principio y al final

Puedes tanto añadir al principio como al final de un bitstring existente utilizando la forma especial. El tipo ::bitstring debe usarse en el bitstring existente si este es de tamaño desconocido.

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

Concatenación

Podemos concatenar bitstrings almacenados en variables utilizando la forma especial. El tipo ::bitstring debe usarse al concatenar dos bitstrings de tamaños desconocidos.

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

Coincidencia de patrones

La coincidencia de patrones también se puede usar para obtener valores de la forma especial. Tienes que conocer el número de bits de cada fragmento que quieras capturar, con una excepción: el tipo ::bitstring se puede usar para hacer coincidencia de patrones en un bitstring de tamaño desconocido, pero solo se puede usar para el último fragmento.

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

Inspeccionar bitstrings

Note

Es posible que los bitstrings se impriman (por el test runner o en iex) en un formato diferente al que se utilizó para crearlos. Esto suele causar confusión al aprender bitstrings.

Por defecto, los bitstrings se muestran en fragmentos de 8 bits (un byte), incluso si los creaste con fragmentos de un tamaño diferente.

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

Si creas un bitstring que representa un string imprimible codificado en UTF-8, se muestra como un string.

<<>>
# => ""

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

Instrucciones

Pintar por números (también conocido como colorear por números) son dibujos en blanco y negro pensados para colorear. Las distintas zonas del dibujo llevan anotados números diferentes, y esos números se corresponden con colores concretos de una paleta de colores predefinida. El objetivo es rellenar las zonas con los colores correctos, y así, al final, aparece un bonito dibujo lleno de color. Es una actividad relajante tanto para niños como para adultos.

Te han encargado escribir una aplicación de pintar por números en Elixir. Quieres que tu aplicación pueda importar y exportar dibujos en un formato de datos personalizado. Has decidido usar ficheros binarios para almacenar los datos de tus dibujos.

Note

Este ejercicio da por supuesto que estás familiarizado con los números binarios y que entiendes los principios que hay detrás de convertir números binarios en números decimales y números decimales en binarios.

Imagina que tienes un dibujo de una cara sonriente, como la que se muestra a continuación. El dibujo tiene un fondo blanco. La cara sonriente tiene el borde negro y el relleno amarillo.

Este dibujo usa 3 colores. Supongamos que asignamos índices a esos colores:

  • 0 (binario: 0b00) para el blanco,
  • 1 (binario: 0b01) para el negro,
  • 2 (binario: 0b10) para el amarillo.

Ahora podemos usar esos índices de color para representar el color de cada píxel.

Cara sonriente Cara sonriente con índices de color

1. Calcular el tamaño en bits de la paleta

Implementa la función PaintByNumber.palette_bit_size/1. Debe recibir el número de colores de la paleta y devolver cuántos bits son necesarios para representar esa cantidad de índices de color como números binarios. Los índices de color siempre empiezan en 0 y son enteros consecutivos que van en orden ascendente.

Por ejemplo, para representar 13 colores diferentes se necesitan 4 bits. 4 bits pueden almacenar hasta 16 índices de color (2^4). 3 bits no serían suficientes, porque 3 bits solo pueden almacenar hasta 8 índices de color (2^3).

PaintByNumber.palette_bit_size(13)
# => 4

Nota: no hay ninguna función log2 en la biblioteca estándar de Elixir. Más adelante aprenderás a usar las bibliotecas de Erlang desde Elixir, donde puedes encontrar esa función. Por ahora, resuelve esta tarea con recursión y el operador de potencia **.

2. Crear un dibujo vacío

Implementa la función PaintByNumber.empty_picture/0. Debe devolver un bitstring vacío.

3. Crear un dibujo de prueba

Para probar manualmente las funcionalidades básicas de tu aplicación se usará un dibujo de prueba predefinido. El dibujo de prueba consta de 4 píxeles con 4 colores diferentes.

Implementa la función PaintByNumber.test_picture/0. Debe devolver un bitstring formado por 4 segmentos. Cada segmento debe tener un tamaño de 2 bits. Los segmentos deben tener los valores 0, 1, 2 y 3.

4. Anteponer un píxel a un dibujo

Implementa la función PaintByNumber.prepend_pixel/3. Debe recibir tres argumentos: un bitstring con el dibujo al que vamos a anteponer, el número de colores de la paleta y el índice del color del nuevo píxel. Debe devolver un bitstring con el dibujo y el nuevo píxel añadido al principio.

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. Obtener el primer píxel de un dibujo

Implementa la función PaintByNumber.get_first_pixel/2. Debe recibir dos argumentos: un bitstring con el dibujo del que estamos leyendo y el número de colores de la paleta. Debe devolver el índice de color del primer píxel del dibujo dado. Cuando se le pasa un dibujo vacío, debe devolver nil.

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

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

6. Descartar el primer píxel de un dibujo

Implementa la función PaintByNumber.drop_first_pixel/2. Debe recibir dos argumentos: un bitstring con el dibujo del que vamos a quitar un píxel y el número de colores de la paleta. Debe devolver el dibujo sin el primer píxel. Cuando se le pasa un dibujo vacío, debe devolver un dibujo vacío.

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. Concatenar dos dibujos

Implementa la función PaintByNumber.concat_pictures/2. Debe recibir dos argumentos, dos bitstrings. Debe devolver un bitstring que sea el resultado de anteponer el primer argumento al segundo.

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>>)
Editar en GitHub El enlace se abre en una ventana o pestaña nueva
Elixir Exercism

¿Listo para empezar Pintar por números?

Regístrate en Exercism para aprender y dominar Elixir con 58 conceptos168 ejercicios y mentoría humana real, todo gratis.