مسیرها
/
Elixir
Elixir
/
تمرین‌ها
/
رنگ‌آمیزی با شماره
رنگ‌آمیزی با شماره

رنگ‌آمیزی با شماره

تمرین یادگیری

مقدمه

رشته‌ی بیتی

کار کردن با داده‌های دودویی مفهومی مهم در هر زبانی است و Elixir روشی شیک برای نوشتن، تطبیق دادن و ساختن داده‌های دودویی در اختیار شما می‌گذارد.

در Elixir به داده‌های دودویی، نوع رشته‌ی بیتی می‌گویند. نوع داده‌ی دودویی (که نباید با داده‌های دودویی به معنای کلی اشتباه گرفته شود) شکل خاصی از رشته‌ی بیتی است که در تمرینی دیگر درباره‌ی آن صحبت می‌کنیم.

لیترال‌های رشته‌ی بیتی با استفاده از شکل ویژه‌ی <<>> تعریف می‌شوند. وقتی یک لیترال رشته‌ی بیتی تعریف می‌کنید، آن را در قالب قطعه‌هایی تعریف می‌کنید. هر قطعه یک مقدار و یک نوع دارد که با عملگر :: از هم جدا می‌شوند. نوع مشخص می‌کند که برای کدگذاری مقدار، چند بیت استفاده می‌شود. می‌توان نوع را کاملاً حذف کرد که در این صورت به‌طور پیش‌فرض یک مقدار صحیح ۸ بیتی خواهد بود.

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

نوشتن نوع به شکل ::1 شکل کوتاه‌شده‌ی ::size(1) است. اگر اندازه‌ی بیت‌ها از یک متغیر بیاید، باید از شکل بلندتر استفاده کنید.

دودویی

وقتی لیترال‌های صحیح دودویی را می‌نویسیم، می‌توانیم آن‌ها را مستقیماً در مبنای ۲ بنویسیم و پیشوند 0b را به لیترال اضافه کنیم. توجه کنید که به هر حال هنگام نمایش در نتایج تست‌ها یا در iex، این اعداد به‌صورت اعداد دهدهی نشان داده می‌شوند.

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

بریدن

اگر مقدار قطعه از ظرفیت نوع آن قطعه فراتر برود، از سمت چپ بریده می‌شود.

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

افزودن به ابتدا و انتها

با استفاده از شکل ویژه می‌توانید هم به ابتدای یک رشته‌ی بیتی موجود چیزی اضافه کنید و هم به انتهای آن. اگر اندازه‌ی رشته‌ی بیتی موجود نامعلوم باشد، باید نوع ::bitstring را برای آن به کار ببرید.

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

به هم چسباندن

می‌توانیم رشته‌های بیتی ذخیره‌شده در متغیرها را با استفاده از شکل ویژه به هم بچسبانیم. هنگام به هم چسباندن دو رشته‌ی بیتی با اندازه‌های نامعلوم باید از نوع ::bitstring استفاده کرد.

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

تطبیق الگو

از تطبیق الگو می‌توان برای گرفتن مقدارها از شکل ویژه هم استفاده کرد. باید تعداد بیت‌های هر تکه‌ای را که می‌خواهید بگیرید بدانید، با یک استثنا: می‌توان از نوع ::bitstring برای تطبیق الگو روی یک رشته‌ی بیتی با اندازه‌ی نامعلوم استفاده کرد، اما این کار فقط برای تکه‌ی آخر امکان‌پذیر است.

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

بررسی رشته‌های بیتی

Note

ممکن است رشته‌های بیتی (توسط اجراکننده‌ی تست یا در iex) در قالبی متفاوت از قالبی که برای ساختنشان استفاده شده چاپ شوند. این موضوع هنگام یادگیری رشته‌های بیتی اغلب باعث سردرگمی می‌شود.

به‌طور پیش‌فرض، رشته‌های بیتی در تکه‌هایی ۸ بیتی (یک بایت) نمایش داده می‌شوند، حتی اگر آن‌ها را با تکه‌هایی با اندازه‌ی متفاوت ساخته باشید.

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

اگر رشته‌ی بیتی‌ای بسازید که نماینده‌ی یک رشته‌ی قابل چاپ کدگذاری‌شده با UTF-8 باشد، به‌صورت یک رشته نمایش داده می‌شود.

<<>>
# => ""

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

دستورالعمل‌ها

نقاشی با شماره (که با اسم رنگ‌آمیزی با شماره هم شناخته می‌شود) تصاویری سیاه‌وسفید برای رنگ‌آمیزی است. روی بخش‌های مختلف تصویر شماره‌های متفاوتی نوشته می‌شود و این شماره‌ها به رنگ‌های مشخصی در یک پالت رنگ از پیش تعریف‌شده اشاره می‌کنند. هدف این است که بخش‌ها را با رنگ‌های درست پر کنید تا در پایان تصویری زیبا و رنگارنگ نمایان شود. این یک فعالیت آرام‌بخش برای کودکان و بزرگسالان است.

قرار است یک برنامه‌ی نقاشی با شماره به زبان Elixir بنویسید. می‌خواهید برنامه‌تان بتواند تصاویر را در یک قالب داده‌ای سفارشی وارد و صادر کند. تصمیم گرفته‌اید داده‌های تصویرتان را در فایل‌های دودویی ذخیره کنید.

Note

این تمرین فرض می‌کند که با اعداد دودویی آشنا هستید و اصول تبدیل اعداد دودویی به اعداد دهدهی و اعداد دهدهی به اعداد دودویی را می‌دانید.

تصور کنید تصویری از یک شکلک لبخند دارید، مثل تصویری که در ادامه می‌بینید. پس‌زمینه‌ی تصویر سفید است. شکلک یک حاشیه‌ی سیاه و یک رنگ پرکننده‌ی زرد دارد.

این تصویر از ۳ رنگ استفاده می‌کند. فرض کنید به این رنگ‌ها اندیس نسبت می‌دهیم:

  • 0 (دودویی: 0b00) برای سفید،
  • 1 (دودویی: 0b01) برای سیاه،
  • 2 (دودویی: 0b10) برای زرد.

حالا می‌توانیم با این اندیس‌های رنگ، رنگ هر پیکسل را نمایش دهیم.

شکلک شکلک با اندیس‌های رنگ

1. محاسبه‌ی اندازه‌ی بیت پالت

تابع PaintByNumber.palette_bit_size/1 را پیاده‌سازی کنید. این تابع باید تعداد رنگ‌های پالت را بگیرد و برگرداند که برای نمایش این تعداد اندیس رنگ به‌صورت اعداد دودویی، چند بیت لازم است. اندیس‌های رنگ همیشه از ۰ شروع می‌شوند و اعداد صحیحی صعودی و پیوسته‌اند.

برای مثال، نمایش ۱۳ رنگ متفاوت به ۴ بیت نیاز دارد. ۴ بیت می‌تواند تا ۱۶ اندیس رنگ (۲ به توان ۴) را ذخیره کند. ۳ بیت کافی نیست، چون ۳ بیت فقط می‌تواند تا ۸ اندیس رنگ (۲ به توان ۳) را ذخیره کند.

PaintByNumber.palette_bit_size(13)
# => 4

توجه: در کتابخانه‌ی استاندارد Elixir تابع log2 وجود ندارد. بعداً یاد می‌گیرید که چطور در Elixir از کتابخانه‌های Erlang استفاده کنید، جایی که می‌توانید این تابع را پیدا کنید. فعلاً این کار را به‌جای آن، با بازگشت و عملگر توان ** حل کنید.

2. ساختن یک تصویر خالی

تابع PaintByNumber.empty_picture/0 را پیاده‌سازی کنید. این تابع باید یک رشته‌ی بیتی خالی برگرداند.

3. ساختن یک تصویر آزمایشی

برای آزمون دستی قابلیت‌های پایه‌ی برنامه‌تان از یک تصویر آزمایشی از پیش تعریف‌شده استفاده می‌شود. تصویر آزمایشی از ۴ پیکسل با ۴ رنگ متفاوت تشکیل شده است.

تابع PaintByNumber.test_picture/0 را پیاده‌سازی کنید. این تابع باید رشته‌ی بیتی‌ای برگرداند که از ۴ بخش تشکیل شده است. اندازه‌ی بیت هر بخش باید ۲ باشد. مقدار بخش‌ها باید ۰، ۱، ۲ و ۳ باشد.

4. افزودن یک پیکسل به ابتدای تصویر

تابع PaintByNumber.prepend_pixel/3 را پیاده‌سازی کنید. این تابع باید سه ورودی بگیرد: یک رشته‌ی بیتی از تصویری که می‌خواهیم به ابتدای آن چیزی اضافه کنیم، تعداد رنگ‌های پالت، و اندیس رنگ پیکسل جدید. خروجی آن باید رشته‌ی بیتی‌ای باشد که پیکسل جدید به ابتدای تصویرش اضافه شده است.

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. گرفتن پیکسل اول از یک تصویر

تابع PaintByNumber.get_first_pixel/2 را پیاده‌سازی کنید. این تابع باید دو ورودی بگیرد: یک رشته‌ی بیتی از تصویری که از آن می‌خوانیم، و تعداد رنگ‌های پالت. باید اندیس رنگ پیکسل اول تصویر داده‌شده را برگرداند. اگر تصویر خالی به آن داده شود، باید nil را برگرداند.

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

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

6. حذف پیکسل اول از یک تصویر

تابع PaintByNumber.drop_first_pixel/2 را پیاده‌سازی کنید. این تابع باید دو ورودی بگیرد: یک رشته‌ی بیتی از تصویری که می‌خواهیم پیکسلی را از آن حذف کنیم، و تعداد رنگ‌های پالت. باید تصویر را بدون پیکسل اول برگرداند. اگر تصویر خالی به آن داده شود، باید یک تصویر خالی برگرداند.

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 را پیاده‌سازی کنید. این تابع باید دو ورودی، یعنی دو رشته‌ی بیتی، بگیرد. باید رشته‌ی بیتی‌ای برگرداند که حاصل افزودن ورودی اول به ابتدای ورودی دوم است.

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 تمرین و مربی‌گری انسانی واقعی یاد بگیرید و در آن استاد شوید، همه‌ی این‌ها رایگان.