الجمعة، 2 ديسمبر، 2011

مطابقة الأنماط في لغة البرمجة كلمات

ملاحظة: هذه الأمثلة تحتاج نسخة كلمات ديسمبر 2011 أو أحدث. يمكن تحميل أحدث نسخة من هنا أو هنا.

مطابقة الأنماط pattern matching هي خاصية في لغات برمجة كثيرة وهي الآن في كلمات أيضاً. سوف نتعرف عليها الآن.

تخيل أنك تريد كتابة إجراء اسمه اطبع.الأسم، يأخذ اسماً في أحد الصور الآتية:
  • مصفوفة من عنصرين: الاسم واسم الأب
  • مصفوفة من ثلاثة عناصر: الاسم، اسم الأب، واسم العائلة على طريقة أهل الخليج العربي
ربما يمكن كتابة هذا الإجراء هكذا:
إجراء اطبع.الاسم ( م ) :
    إذا م هو مصفوفة.قيم وأيضا عدد( م ) = 2 :
        الاسم = م [ 1 ] 
        اسم.الأب = م [ 2 ] 
        اطبع الاسم ، " " ، اسم.الأب 
    وإلا إذا م هو مصفوفة.قيم وأيضا عدد( م ) = 3 :
        الاسم = م [ 1 ] 
        اسم.الأب = م [ 2 ] 
        اسم.العائلة = م [ 3 ] 
        اطبع الاسم ، " بن " ، اسم.الأب ، " آل " ، اسم.العائلة 
    تم 
نهاية

لكن هذه الكود تبدو رتيبة ومتكررة، التأكد ان القيمة المقدمة مصفوفة، التأكد من العدد، تفكيك البيانات إلى متغيرات...
هذه النوعية من المهام هي الدور الطبيعي لمطابقة الأنماط! هيا نكتب نفس الإجراء بالطريقة الجديدة:
إجراء اطبع.الاسم ( م ) :
    إذا م ~ [ ؟الاسم ، ؟اسم.الأب ] :
        اطبع الاسم ، " " ، اسم.الأب 
    وإلا إذا م ~ [ ؟الاسم ، ؟اسم.الأب ، ؟اسم.العائلة ] :
        اطبع الاسم ، " بن " ، اسم.الأب ، " آل " ، اسم.العائلة 
    تم 
نهاية

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

لكن ماذا لو كان يمكن أيضاً أن يقدم للإجراء - بجانب القيم السابقة - كائن من نوع "شخص" له حقلين هما الاسم واسم الأب؟ يمكننا تعديل البرنامج كالآتي:
فصيلة شخص :
    له اسم ، اسم.أب 
تم 

إجراء اطبع.الاسم ( م ) :
    إذا م ~ [ ؟ الاسم ، ؟ اسم.الأب ] :
        اطبع الاسم ، " " ، اسم.الأب 
     إذا م ~ [ ؟ الاسم ، ؟ اسم.الأب ، ؟ اسم.العائلة ] :
        اطبع الاسم ، " بن " ، اسم.الأب ، " آل " ، اسم.العائلة 
    وإلا إذا م ~ شخص له اسم = ؟ أ ، اسم.أب = ؟ ب :
        اطبع أ ، " " ، ب 
    تم 
نهاية
لاحظ كيف ان قراءة هذا الجزء من البرنامج سلسة جداً: "إذا كان م يطابق شخصاً له اسمٌ يساوي أ، واسم أبٍ يساوي ب، افعل كذا وكذا". شيء جميل جداً في رأيي :)

أخيراً ماذا لو كان يمكن للإجراء أيضاً أن يقبل قاموساً (مثل hashtable) فيه الاسم واسم الأب، وقد يكون أو لا يكون فيه اسم العائلة؟؟
كل مشكلة لها حل :)
إجراء اطبع.الاسم( م ) :
    إذا م ~ [ ؟ الاسم ، ؟ اسم.الأب ] :
        اطبع الاسم ، " " ، اسم.الأب 
    وإلا إذا م ~ [ ؟ الاسم ، ؟ اسم.الأب ، ؟ اسم.العائلة ] :
        اطبع الاسم ، " بن " ، اسم.الأب ، " آل " ، اسم.العائلة 
    وإلا إذا م ~ شخص له اسم = ؟ أ ، اسم.أب = ؟ ب :
        اطبع أ ، " " ، ب 
    وإلا إذا م ~ {"الاسم" => ؟أ، "اسم.الأب" => ؟ب، "اسم.العائلة" => ؟ج}:
        اطبع أ ، " بن " ، ب ، " آل " ، ج 
    وإلا إذا م ~ { "الاسم" => ؟أ ، "اسم.الأب" => ؟ب } :
        اطبع أ ، " " ، ب 
    تم 
نهاية

لاحظ أنه قد كان يجب اختبار حالة اسم العائلة أولاً قبل حالة عدم وجود اسم عائلة، وذلك لأن مطابقة القواميس تتأكد فقط أن القيم المطلوبة موجودة، وليس إذا كانت القيم الوحيدة الموجودة!

هل تبقى شيء؟ أجل:

- يمكن المطابقة بالقيم العادية، مثلاً م ~ 12 ، أو مثل م ~ "مرحبا"

يمكن مطابقة مصفوفة بحيث لا يهم طولها، بل فقط قيم معينة تبدأ بها، هكذا:
إذا م ~ ["شجرة"، ؟ب، ...] :
    <افعل كذا>
تم
 هذا يطابق م مع مصفوفة أول عنصر فيها "شجرة"، ويضع ثاني عنصر في المتغير ب، ولا يهمه باقي العناصر، فقط ينبغي أن يكون عددها اثنان أو أكثر.
- علامة ~ على لوحة المفاتيح العربية هي shift + ئ

- وآخر شيء هو أن مطابقة الأنماط يمكن تبييتها nesting بحيث يمكن مثلاً مطابقة إذا كانت القيمة مصفوفة أول عنصر فيها كائن من نوع كذا وله حقل اسمه كذا....الخ.

‏هناك تعليقان (2):

Omar Ayman يقول...

رائع كالعادة :) أنا فخور اني اعرف حضرتك :)

Moha Salem يقول...

شكرا