المسارات
/
Elixir
Elixir
/
التمارين
/
التلوين بالأرقام
التلوين بالأرقام

التلوين بالأرقام

تمرين تعلّمي

مقدمة

السلاسل البتية

يُعدّ العمل مع البيانات الثنائية مفهومًا مهمًا في أي لغة، ويوفّر Elixir صياغة أنيقة لكتابة البيانات الثنائية ومطابقتها وبنائها.

في Elixir، يُشار إلى البيانات الثنائية بنوع السلسلة البتية. أما نوع البيانات الثنائية (ولا يُخلط بينه وبين البيانات الثنائية عمومًا) فهو شكل خاص من السلسلة البتية، وسنناقشه في تمرين لاحق.

تُعرَّف القيم الحرفية للسلاسل البتية باستخدام الصيغة الخاصة <<>>. وعند تعريف قيمة حرفية لسلسلة بتية، فإنها تُعرَّف على شكل مقاطع. لكل مقطع قيمة ونوع، يفصل بينهما العامل ::. ويحدد النوع عدد البتات التي ستُستخدم لترميز القيمة. ويمكن حذف النوع تمامًا، وفي هذه الحالة يكون الافتراضي قيمة عدد صحيح من 8 بتات.

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

كتابة النوع بالصيغة ::1 اختصار لكتابة ::size(1). عليك استخدام الصيغة الأطول إذا كان حجم البتات قادمًا من متغير.

البيانات الثنائية

عند كتابة القيم الحرفية للأعداد الصحيحة الثنائية، يمكننا كتابتها مباشرة بصيغة الأساس 2 بوضع البادئة 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) بصيغة مختلفة عن الصيغة التي أُنشئت بها. وهذا غالبًا ما يسبّب الارتباك عند تعلّم السلاسل البتية.

افتراضيًا، تُعرض السلاسل البتية في مقاطع من 8 بتات (بايت)، حتى لو أنشأتها بمقاطع بحجم مختلف.

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

إذا أنشأت سلسلة بتية تمثّل سلسلة نصية قابلة للطباعة بترميز UTF-8، فستُعرض على هيئة سلسلة نصية.

<<>>
# => ""

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

التعليمات

التلوين بالأرقام (وتُعرف أيضًا بـ التلوين حسب اللون) هي صور بالأبيض والأسود مخصّصة للتلوين. تُعلَّم مناطق الصورة المختلفة بأرقام مختلفة، وتقابل هذه الأرقام ألوانًا محدّدة في لوحة ألوان معرّفة مسبقًا. والهدف هو ملء المناطق بالألوان الصحيحة، لتكشف في النهاية عن صورة جميلة ملوّنة. إنه نشاط مريح للأطفال والبالغين على حد سواء.

أُوكلت إليك مهمة كتابة تطبيق للتلوين بالأرقام بلغة Elixir. تريد أن يكون تطبيقك قادرًا على استيراد الصور وتصديرها بصيغة بيانات مخصّصة. وقد قرّرت استخدام الملفات الثنائية لتخزين بيانات صورتك.

Note

يفترض هذا التمرين أنك على دراية بـالأعداد الثنائية وأنك تفهم المبادئ الكامنة وراء تحويل الأعداد الثنائية إلى أعداد عشرية وتحويل الأعداد العشرية إلى أعداد ثنائية.

لنتخيّل أن لديك صورة لوجه مبتسم، كالتي تظهر أدناه. للصورة خلفية بيضاء، وللوجه المبتسم إطار أسود ولون تعبئة أصفر.

تستخدم هذه الصورة 3 ألوان. ولنفترض أننا نسنِد فهارس إلى هذه الألوان:

  • 0 (ثنائي: 0b00) للأبيض،
  • 1 (ثنائي: 0b01) للأسود،
  • 2 (ثنائي: 0b10) للأصفر.

يمكننا الآن استخدام فهارس الألوان هذه لتمثيل لون كل بكسل.

الوجه المبتسم الوجه المبتسم مع فهارس الألوان

1. احسب عدد بتات لوحة الألوان

نفّذ الدالة PaintByNumber.palette_bit_size/1. ينبغي أن تأخذ عدد الألوان في لوحة الألوان وتُرجع عدد البتات اللازمة لتمثيل هذا العدد من فهارس الألوان كأعداد ثنائية. تبدأ فهارس الألوان دائمًا من 0 وهي أعداد صحيحة متتالية تصاعديًا.

على سبيل المثال، يتطلّب تمثيل 13 لونًا مختلفًا 4 بتات. تستطيع 4 بتات تخزين ما يصل إلى 16 فهرس لون (2^4). ولن تكفي 3 بتات، لأن 3 بتات لا تستطيع تخزين سوى 8 فهارس ألوان (2^3).

PaintByNumber.palette_bit_size(13)
# => 4

ملاحظة: لا توجد دالة log2 في المكتبة القياسية لـ Elixir. ستتعلّم لاحقًا كيف تستخدم مكتبات Erlang من داخل Elixir، حيث يمكنك العثور على هذه الدالة. والآن، حلّ هذه المهمة باستخدام الاستدعاء الذاتي وعامل الأس ** بدلًا من ذلك.

2. أنشئ صورة فارغة

نفّذ الدالة PaintByNumber.empty_picture/0. ينبغي أن تُرجع سلسلة بتات فارغة.

3. أنشئ صورة اختبار

ستُستخدم صورة اختبار معرّفة مسبقًا للاختبار اليدوي للميزات الأساسية لتطبيقك. تتكوّن صورة الاختبار من 4 بكسلات و4 ألوان مختلفة.

نفّذ الدالة PaintByNumber.test_picture/0. ينبغي أن تُرجع سلسلة بتات تتكوّن من 4 مقاطع. يجب أن يكون حجم كل مقطع 2 بت. ويجب أن تكون قيم المقاطع 0 و1 و2 و3.

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 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.