يُعدّ العمل مع البيانات الثنائية مفهومًا مهمًا في أي لغة، ويوفّر 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
قد تُطبع السلاسل البتية (بواسطة مشغّل الاختبارات أو في iex) بصيغة مختلفة عن الصيغة التي أُنشئت بها. وهذا غالبًا ما يسبّب الارتباك عند تعلّم السلاسل البتية.
افتراضيًا، تُعرض السلاسل البتية في مقاطع من 8 بتات (بايت)، حتى لو أنشأتها بمقاطع بحجم مختلف.
<<2011::11>>
# => <<251, 3::size(3)>>
إذا أنشأت سلسلة بتية تمثّل سلسلة نصية قابلة للطباعة بترميز UTF-8، فستُعرض على هيئة سلسلة نصية.
<<>>
# => ""
<<65, 66, 67>>
# => "ABC"
التلوين بالأرقام (وتُعرف أيضًا بـ التلوين حسب اللون) هي صور بالأبيض والأسود مخصّصة للتلوين. تُعلَّم مناطق الصورة المختلفة بأرقام مختلفة، وتقابل هذه الأرقام ألوانًا محدّدة في لوحة ألوان معرّفة مسبقًا. والهدف هو ملء المناطق بالألوان الصحيحة، لتكشف في النهاية عن صورة جميلة ملوّنة. إنه نشاط مريح للأطفال والبالغين على حد سواء.
أُوكلت إليك مهمة كتابة تطبيق للتلوين بالأرقام بلغة Elixir. تريد أن يكون تطبيقك قادرًا على استيراد الصور وتصديرها بصيغة بيانات مخصّصة. وقد قرّرت استخدام الملفات الثنائية لتخزين بيانات صورتك.
يفترض هذا التمرين أنك على دراية بـالأعداد الثنائية وأنك تفهم المبادئ الكامنة وراء تحويل الأعداد الثنائية إلى أعداد عشرية وتحويل الأعداد العشرية إلى أعداد ثنائية.
لنتخيّل أن لديك صورة لوجه مبتسم، كالتي تظهر أدناه. للصورة خلفية بيضاء، وللوجه المبتسم إطار أسود ولون تعبئة أصفر.
تستخدم هذه الصورة 3 ألوان. ولنفترض أننا نسنِد فهارس إلى هذه الألوان:
0 (ثنائي: 0b00) للأبيض،1 (ثنائي: 0b01) للأسود،2 (ثنائي: 0b10) للأصفر.يمكننا الآن استخدام فهارس الألوان هذه لتمثيل لون كل بكسل.
| الوجه المبتسم | الوجه المبتسم مع فهارس الألوان |
|---|---|
![]() |
![]() |
نفّذ الدالة 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، حيث يمكنك العثور على هذه الدالة. والآن، حلّ هذه المهمة باستخدام الاستدعاء الذاتي وعامل الأس ** بدلًا من ذلك.
نفّذ الدالة PaintByNumber.empty_picture/0. ينبغي أن تُرجع سلسلة بتات فارغة.
ستُستخدم صورة اختبار معرّفة مسبقًا للاختبار اليدوي للميزات الأساسية لتطبيقك. تتكوّن صورة الاختبار من 4 بكسلات و4 ألوان مختلفة.
نفّذ الدالة PaintByNumber.test_picture/0. ينبغي أن تُرجع سلسلة بتات تتكوّن من 4 مقاطع.
يجب أن يكون حجم كل مقطع 2 بت. ويجب أن تكون قيم المقاطع 0 و1 و2 و3.
نفّذ الدالة 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>>)
نفّذ الدالة PaintByNumber.get_first_pixel/2. ينبغي أن تأخذ وسيطين: سلسلة بتات تمثّل الصورة التي نقرأ منها، وعدد الألوان في لوحة الألوان. وينبغي أن تُرجع فهرس لون أول بكسل في الصورة المعطاة. وعند تمرير صورة فارغة، ينبغي أن تُرجع nil.
picture = <<19::5, 2::5, 18::5>>
color_count = 20
PaintByNumber.get_first_pixel(picture, color_count)
# => 19
نفّذ الدالة 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>>)
نفّذ الدالة 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>>)
سجّل في Exercism لتتعلّم وتتقن Elixir عبر 58 مفهومًا168 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.