تتشارك أغلب لغات البرمجة في العديد من المزايا. من أفضل الطرق لتعلم لغة برمجة جديدة هي كتابة برنامج مألوف. في هذا المقال، سنتعلم كيف ننشئ لعبة "احزر الرقم" باستخدام لغة البرمجة Awk لنشرح من خلالها مفاهيم مألوفة.
عند تعلم البرمجة بلغة برمجة جديدة، من المهم التركيز على أكثر المفاهيم التي تشترك بها لغات البرمجة:
- المتغيرات: مكان يُخزن فيه المعلومات.
- التعابير: طرق لحساب الأشياء.
- الصياغات: طرق التعبير عن تغير الحالة داخل البرنامج.
هذه المفاهيم هي أساس معظم لغات البرمجة.
حالما تستوعب جيدا تلك المفاهيم، ستكون قادرًا على استيعاب البقية منها. مثلا أغلب لغات البرمجة تمتلك "طريقة لفعل الأشياء" مدعومة بطريقة تصميم اللغة، وهذه الطرق تختلف من برنامج لآخر. هذه الطرق تتضمن إنشاء الوحدات (تجميع العمليات المتقاربة مع بعضها)، الصيغة التصريحية وصيغة الأمر، والتوجه الغرضي، والمزايا النحوية عالية ومنخفضة المستوى، وما إلى ذلك. مثال يألفه العديد من المبرمجين وهو "المراسم" ويعني كمية التحضيرات اللازمة قبل البدء بحل المشكلة. لغة جافا تتطلب كمية مراسم كبيرة، متجذرة في تصميمها، والذي يتطلب أن تكون كل الشيفرات معرفة ضمن أصناف.
بالرجوع إلى الأساسيات. لغات البرمجة تتشارك بعدة تشابهات. حالما تتعرف على لغة برمجة واحدة، ابدأ بتعلم أساسيات لغة برمجة أخرى وستجد نفسك تقدّر الاختلافات في تلك اللغة الجديدة.
طريقة جيدة للبدء تكون في إنشاء برنامج اختبار بسيط. أحد تلك البرامج قد يكون برنامج "احزر الرقم". يقوم الحاسب باختيار رقم بين واحد ومئة ويسألك أن تحزر ذلك الرقم. يعيد البرنامج الكرّة إلى أن تحزر الرقم.
برنامج "احزر الرقم" سيُدربك على عدة مفاهيم في لغات البرمجة:
- المتغيرات.
- الدخل.
- الخرج.
- التقييم الشرطي.
- الحلقات التكرارية.
هذا مثال عملي تجريبي رائع لتعلم لغة برمجة جديدة.
احزر الرقم باستخدام awk
لنبدأ بكتابة لعبة "احزر الرقم" كبرنامج في Awk.
Awk لغة برمجة نصية ديناميكية النوع موجهة لتحويل البيانات، ولديها دعم جيد من ناحية الاستخدام التفاعلي. Awk موجودة منذ عام 1970، بدأت كقسم من نظام يونكس. إذا كنت لا تعلم عن Awk ولكنك تحب جداول البيانات فهذه فأنت في المكان الصحيح للتعرف عليها.
يمكنك بدء الاستكشاف عبر كتابة نسخة من لعبة "احزر الرقم". التالي تضمين للعبة (مع ترقيم الأسطر حتى نتمكن من الإشارة إليها لاحقًا):
1 BEGIN { 2 srand(42) 3 randomNumber = int(rand() * 100) + 1 4 print "random number is",randomNumber 5 printf "guess a number between 1 and 100\n" 6 } 7 { 8 guess = int($0) 9 if (guess < randomNumber) { 10 printf "too low, try again:" 11 } else if (guess > randomNumber) { 12 printf "too high, try again:" 13 } else { 14 printf "that's right\n" 15 exit 16 } 17 }
يمكن الملاحظة مباشرة التشابه بين بنى التحكم في Awk ومثيلاتها في لغات أخرى مثل سي C وجافا JAVA، ولا تشبه تلك التي في بايثون. في التعابير مثل if-then-else
أو while
، يمكن أن تأخذ تعبيرًا واحدًا أو مجموعة من التعابير محاطة بقوسين معقوصين {
و }
. ومع ذلك يوجد فرق واحد كبير في Awk يجب فهمه منذ البداية:
اقتباسلغة Awk بتصميمها مبنية حول قنوات أو أنابيب البيانات.
ماذا يعني ذلك؟ معظم برامج Awk هي مقاطع من الشيفرات التي تتلقى دخل، تقوم بعمل ما على البيانات، ثم تكتبها إلى الخرج. لغة Awk توفر افتراضيًا كل احتياجات التحويل والتوصيل لذلك. لنستكشف ذلك معا من خلال البرنامج السابق ونسأل السؤال التالي: أين بنية "القراءة من الطرفية"؟
الجواب أنها مبنية في داخل اللغة نفسها. تحديدا في الأسطر 7 - 17 نخبر Awk ماذا يفعل بكل سطر من الدخل. سيصبح واضحا أن الأسطر 1 - 6 تنفذ قبل أن يتم قراءة أي شيء.
تحديدا الكلمة المفتاحية BEGIN
في السطر الأول هي نمط مكرر، في حالتنا هذه فإنها تخبر Awk قبل قراءة أي شيء أن ينفذ ما بين القوسين {...}
. ومثلها الكلمة المفتاحية END
، لم نستخدمها هنا لكنها تدل Awk على التعليمات التي يجب أن ينفذها بعد أن يتم قراءة كل شيء.
بالعودة للأسطر 7 - 17، فهي تمثل كتلة واحدة ستنفذ معًا، لكن لا يوجد قبلها كلمة مفتاحية. لذا يقوم Awk بتنفيذ هذه الأسطر على كل سطر يتم تلقيه من الدخل وفي حالتنا هذه هي محاولات المستخدم لحزر الرقم.
لنلق الآن نظرة على الشيفرة التي تنفذ، بدايةً هناك المقدمة التي تنفذ قبل كل قراءة للدخل.
في السطر 2، نعرّف رقما عشوائيا من واحد إلى 42. السطر 3 يقوم بحساب رقم بين 1 و 100، السطر 4 يطبع هذه الرقم لنا فقط للتأكد من وجود أي خطأ. السطر 5 يسأل المستخدم أن يحزر رقمًا. لاحظ هنا استخدمنا التابع printf
وليس print
فهو مثل التابع الموجود في C، ويأخذ أول معامل ويكون قالب نصي يستخدم لتنسيق الخرج.
الآن بعد أن تم سؤال المستخدم البرنامج ينتظر الإدخال، بعد أن يدخل المستخدم تخمينه، يمرر Awk ذلك الادخال الى الاسطر 7 - 17، كما ذكرنا سابقًا. السطر 1 يحول الدخل الى نوع عدد صحيح؛ 0$
تعني سجل الدخل كاملًا، بينما 1$
تعني أول حقل من سجل الدخل 2$
تعني الحقل الثاني، وهكذا. يقوم Awk بتقسيم الدخل إلى حقول تلقائيا مستخدمًا الفاصل المحدد مسبقًا، والذي تكون قيمته الافتراضية المساحة الفارغة. الأسطر 9 - 15 تقارن جواب المستخدم مع العدد العشوائي المخزن سابقًا، ويطبع الجواب المناسب. إذا كان الجواب صحيحًا، السطر 15 سيخرج من البرنامج وينهي سلسلة معالجة الدخل، بهذه البساطة.
بالأخذ بالحسبان بنية Awk الغريبة التي يتميز بها، فهو يتكون من مقاطع برمجية تتجاوب مع أسطر دخل بإعدادات معينة وتقوم بفعل الأشياء بها، لننظر الآن إلى بنية مختلفة لنرى كيف يعمل قسم التصفية:
1 BEGIN { 2 srand(42) 3 randomNumber = int(rand() * 100) + 1 4 print "random number is",randomNumber 5 printf "guess a number between 1 and 100\n" 6 } 7 int($0) < randomNumber { 8 printf "too low, try again: " 9 } 10 int($0) > randomNumber { 11 printf "too high, try again: " 12 } 13 int($0) == randomNumber { 14 printf "that's right\n" 15 exit 16 }
الأسطر 1 - 6 لم تتغير عن البرنامج السابق لكن نلاحظ أن الشيفرة في الأسطر 7 - 9 ستنفذ في حال كان جواب المستخدم أقل من الرقم العشوائي الذي يحاول تخمينه، والأسطر 10 - 12 كذلك ستنفذ في حال كان جواب المستخدم أكبر من الرقم العشوائي الذي يحاول المستخدم تخمينه، والشيفرة في الأسطر 13 - 16 ستنفذ في حال كان جواب المستخدم مساويًا لذلك الرقم.
قد يبدو ذلك غريبًا، لماذا نقوم بتكرار حساب int($0)
عند كل تحقق؟ فهي طريقة غريبة لحل المشكلة. لكن هذه الأنماط هي طريقة رائعة لفصل معالجة التحقق من الإجابة حيث يمكن توظيف استخدام التعابير النظامية أو أي بنية مدعومة من قبل Awk عند كل كتلة من كتل التحقق تلك بشكل منفصل.
يمكننا أيضا استخراج الحسابات المتكررة وفصلها بمكان واحد، عن أماكن التحقق منها للحالات المختلفة، التالي نسخة ثالثة من البرنامج توضح ذلك:
1 BEGIN { 2 srand(42) 3 randomNumber = int(rand() * 100) + 1 4 print "random number is",randomNumber 5 printf "guess a number between 1 and 100\n" 6 } 7 { 8 guess = int($0) 9 } 10 guess < randomNumber { 11 printf "too low, try again: " 12 } 13 guess > randomNumber { 14 printf "too high, try again: " 15 } 16 guess == randomNumber { 17 printf "that's right\n" 18 exit 19 }
لاحظ أنه مهما كانت القيمة المدخلة من قبل المستخدم، يجب تحويلها إلى عدد صحيح، تم إضافة الأسطر 7 - 9 لفعل ذلك. الآن الثلاث مجموعات من الأسطر 10 - 12، 13 - 15 و 16 - 19، تشير إلى متغير معرّف مسبقًا باسم guess
بدلًا من تحويل الدخل عندها في كل مرة.
لنقم بمراجعة لما كنا قد نوينا تعلمه سابقًا:
- المتغيرات: Awk يحتوي عليها؛ يمكننا الملاحظة أن بيانات الدخل تأتي إلى البرنامج على شكل نص ولكن يمكننا تحويلها إلى قيمة عددية عند الحاجة.
- الدخل: يقوم Awk بإرسال الدخل عبر طريقته "سلسلة معالجة البيانات" لقراءة الدخل.
-
الخرج: قمنا باستخدام توابع Awk الإجرائية التالية
print
وprintf
لكتابة الأشياء إلى الخرج. -
التقييم الشرطي: تعلمنا عن كتلة
if-then-else
في Awk وتصفية الدخل التي تتجاوب مع إعدادات مختلفة لأسطر الدخل. -
الحلقات: لم نحتج إلى الحلقات في مثالنا هذا، لكن بفضل طريقة Awk في معالجة البيانات عبر "سلسلة معالجة البيانات" فإن الحلقات موجودة من طبيعة تصميم اللغة نفسها. لكن يمكن للمستخدم الخروج من تلك السلسلة في أي وقت عبر إرسال إشارة "نهاية الملف" إلى Awk (بالضغط على
Ctrl+D
عند استعمال طرفية لينكس).
من المهم ملاحظة عدم حاجتنا للحلقات لمعالجة الدخل. أحد أهم الأسباب التي جعلت Awk يبقى طويلًا هي أن برامجه تكون عادةً قصيرة، وأحد أسباب كونها قصيرة هي أنه لا يوجد شيفرة متداولة مكررة كثيرا وضرورية للقراءة من الطرفية أو من ملف مثلًا.
لننفذ البرنامج:
$ awk -f guess.awk random number is 25 guess a number between 1 and 100: 50 too high, try again: 30 too high, try again: 10 too low, try again: 25 that's right $
من الأشياء التي لم نذكرها هي التعليقات. التعليق في Awk يبدأ بمحرف المربع (#
) وينتهي عند نهاية سطر التعليق.
الختام
Awk لغة قوية ومثالنا السابق "لعبة احزر الرقم" هو بداية جيدة لبدء تعلم البرمجة بتلك اللغة. وهي البداية وليست نهاية طريق تعلمك لها، يمكنك أيضا القراءة عن تاريخ لغة Awk و Gawk (إصدار awk لجنو)، وهي إصدار موسع من Awk وعلى الأغلب هو الإصدار المتواجد على حاسبك إذا كنت تعمل على نظام لينكس، أو يمكنك.
ترجمة -وبتصرف- للمقال Learn awk by coding a "guess the number" game لصاحبه Chris Hermansen.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.