المسارات
/
Crystal
Crystal
/
التمارين
/
حاسبة المطبخ
حاسبة المطبخ

حاسبة المطبخ

تمرين تعلّمي

مقدمة

الصف هو قائمة مرتّبة ومنتهية من العناصر، وهو غير قابل للتغيير. ويشترط الصف أن يكون لكل موضع نوع ثابت. وهذا يعني بدوره أن المترجم يعرف نوع كل موضع. يمكن أن تختلف الأنواع المستخدمة في الصف من موضع إلى آخر، لكن يجب أن تكون الأنواع معروفة في وقت الترجمة.

إنشاء صف

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

tuple = {1, "foo", 'c'} # Tuple(Int32, String, Char)

ويمكنك أيضًا إنشاء صف باستخدام الصنف Tuple.

tuple = Tuple(Int32, String, Char).new(1, "foo", 'c')

أو يمكنك تحديد نوع المتغير الذي يُسنَد إليه الصف صراحةً.

tuple : Tuple(Int32, String, Char) = {1, "foo", 'c'}

قد يكون تحديد نوع الصف صراحةً مفيدًا، فهو يسمح بتعريف موضع يحمل نوعًا اتحاديًا. وهذا يعني أن الموضع الواحد يمكن أن يحمل عدة أنواع.

tuple : Tuple(Int32 | String, String, Char) = {1, "foo", 'c'}

التحويل

إنشاء صف من مصفوفة

يمكنك إنشاء صف من مصفوفة باستخدام الطريقة from في الصنف Tuple. ويتطلب ذلك تحديد نوع الصف.

array = [1, "foo", 'c']
tuple = Tuple(Int32, String, Char).from(array)

التحويل إلى مصفوفة

يمكنك تحويل الصف إلى مصفوفة باستخدام الطريقة to_a. نوع عناصر المصفوفة الناتجة هو اتحاد أنواع الحقول في الصف.

tuple = {1, "foo", 'c'}
array = tuple.to_a
array # => [1, "foo", 'c']

الوصول إلى العناصر

مثل المصفوفات، تفهرس الصفوف بدءًا من الصفر، أي أن العنصر الأول عند الفهرس 0. لكن على عكس المصفوفات، نوع كل عنصر ثابت ومعروف في وقت الترجمة، ولذلك عندما تفهرس صفًا فإن نوع العنصر يخص الموضع نفسه. للوصول إلى عنصر في صف، يمكنك استخدام العامل [].

array = [1, "foo", 'c']
array[0]         # => 1
typeof(array[0]) # => Int32 | String | Char

tuple = {1, "foo", 'c'}
tuple[0]         # => 1
typeof(tuple[0]) # => Int32

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

tuple = {1, "foo", 'c'}
tuple[3]
# => Error: index out of bounds for Tuple(Int32, String, Char) (3 not in -3..2)

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

الصف الفرعي

يمكنك الحصول على صف فرعي من صف باستخدام العامل [] مع مدى. والناتج صف جديد يضم العناصر ضمن المدى المحدد. يجب تحديد المدى في وقت الترجمة، وإلا فلن يتمكن المترجم من معرفة أنواع العناصر في الصف الفرعي. وهذا يعني أن المدى يجب أن يكون مدى حرفيًا، لا مسندًا إلى متغير.

tuple = {1, "foo", 'c'}
subtuple = tuple[0..1] # Tuple(Int32, String)

i = 0..1
tuple[i]
# Error: Tuple#[](Range) can only be called with range literals known at compile-time

متى تستخدم الصف

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

لا ينبغي استخدام الصفوف عندما تحتاج إلى بنية بيانات يمكن أن يزيد حجمها أو ينقص، أو تحتاج إلى تعديل متكرر.

الرموز هي معرّفات مسمّاة يمكن استخدامها للإشارة إلى قيمة. تُنشأ الرموز من خلال رمز حرفي، وذلك بوضع المحرف : قبل الاسم، مثل :foo. كما يمكن كتابتها بين علامات تنصيص، مثل :"foo"، مما يسمح مثلًا بوجود مسافات في الاسم.

:foo           # => :foo
:"foo boo"     # => :"foo boo"
:foo == :"foo" # => true

تُستخدم الرموز في مواضع كثيرة في اللغة، بما في ذلك كمفاتيح في الصفوف المسمّاة، ولتمثيل أسماء الطرق والمتغيرات.

الرموز في Crystal

تختلف الرموز في Crystal اختلافًا كبيرًا عنها في Ruby. ففي Crystal، الرمز نوع من الثوابت، ولذلك يُسنَد في وقت الترجمة. وهذا يعني أنه لا يمكن إنشاء الرموز ديناميكيًا، وهو أمر ممكن في Ruby.

تُمثَّل الرموز في Crystal على هيئة Int32، ما يجعلها فعّالة جدًا.

التحويل

يمكن تحويل الرموز إلى سلاسل نصية، لكن ليس العكس. وذلك لأن الرموز تُنشأ في وقت الترجمة، أما السلاسل النصية فتُنشأ في وقت التشغيل.

:foo.to_s # => "foo"

التعليمات

بينما كنت تستعد لخبز الكوكيز لأصدقائك، اكتشفت أن عليك تحويل بعض القياسات المستخدمة في الوصفة. وبما أنك لا تعرف سوى النظام المتري، فأنت بحاجة إلى إيجاد طريقة لتحويل قياسات الخبز الأمريكية الشائعة إلى المليلتر (mL) لتسهيل الأمر عليك.

استخدم جدول التحويل التالي في حلك:

الوحدة المراد تحويلها الحجم بالمليلتر (mL)
mL 1 1
كوب أمريكي 1 240
أونصة سائلة أمريكية 1 30
ملعقة صغيرة أمريكية 1 5
ملعقة كبيرة أمريكية 1 15

وبما أنك مبرمج موهوب في طور التدريب، فقد قررت استخدام المليلتر كوحدة وسيطة لتسهيل التحويل من أي وحدة مذكورة إلى أي وحدة أخرى (حتى إلى نفسها).

1. استخراج المكوّن الرقمي من زوج الحجم

نفّذ الطريقة KitchenCalculator.get_volume. عند تمرير زوج الحجم، ينبغي أن تُرجع المكوّن الرقمي فقط.

KitchenCalculator.get_volume({:cup, 2.0})
# => 2.0

2. تحويل زوج الحجم إلى مليلترات

نفّذ الطريقة KitchenCalculator.to_milliliter. عند تمرير زوج الحجم، ينبغي أن تُحوّل الحجم إلى مليلترات باستخدام جدول التحويل.

KitchenCalculator.to_milliliter({:cup, 2.5})
# => {:milliliter, 600.0}

3. تحويل زوج الحجم بالمليلتر إلى وحدة أخرى

نفّذ الطريقة KitchenCalculator.from_milliliter. عند تمرير زوج الحجم والوحدة المطلوبة، ينبغي أن تُحوّل الحجم إلى الوحدة المطلوبة باستخدام جدول التحويل.

KitchenCalculator.from_milliliter({:milliliter, 1320.0}, :cup)
# => {:cup, 5.5}

4. التحويل من أي وحدة إلى أي وحدة

نفّذ الطريقة KitchenCalculator.convert. عند تمرير زوج الحجم والوحدة المطلوبة، ينبغي أن تُحوّل الحجم المُعطى إلى الوحدة المطلوبة.

KitchenCalculator.convert({:teaspoon, 9.0}, :tablespoon)
# => {:tablespoon, 3.0}
تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة
Crystal Exercism

مستعد لبدء حاسبة المطبخ؟

سجّل في Exercism لتتعلّم وتتقن Crystal عبر 26 مفهومًا142 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.