الخميس، 29 نوفمبر 2012

هيا نستخدم تعبيرات لامدا(1): الترتيب والمقارنة



 هب أن لدينا هذا الإجراء الذي يرتب مصفوفة من الأعداد (الإجراء بطيء لكنه بسيط بما يكفي للأهداف التعليمية):


إجراء رتب (م):
    لكل أ من 1 إلى عدد (م):
        لكل ب من أ + 1 إلى عدد (م):
            إذا م [أ] > م [ب]:
                مؤقت = م [أ]
                م [أ]= م [ب]
                م [ب]= مؤقت 
            تم 
        تابع 
    تابع 
نهاية 

م = [5، 12، 8، 4، 3، 9]
رتب (م)
اطبع م

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

الإجراء يأخذ عاملاً parameter هو المصفوفة 'م'، ماذا لو كان يأخذ عاملاً آخر هو الدالة التي يستخدمها في سطر المقارنة؟
إجراء رتب (م، دالة.المقارنة):
    لكل أ من 1 إلى عدد (م):
        لكل ب من أ + 1 إلى عدد (م):
            إذا دالة.المقارنة : تنفيذها (م [أ]، م [ب]):
                مؤقت = م [أ]
                م [أ]= م [ب]
                م [ب]= مؤقت 
            تم 
        تابع 
    تابع 
نهاية 

م = [5، 12، 8، 4، 3، 9]
رتب (م، λ س، ص : س > ص)
اطبع م

لقد قمنا بثلاث تغييرات صغيرة:
  1. أضفنا عاملاً هو دالة.المقارنة
  2. بدلاً من الاختبار م[أ] > م[ب]، جعلناه دالة.المقارنة: تنفيذها(م[أ]، م[ب])
  3. عند استدعاء الإجراء رتب، أعطيناه دالة مقارنة هي λ س، ص : س > ص، أي الدالة التي لو أخذت س و ص تعود بالقيمة س>ص (القيمة 'صحيح' أو 'خطأ' حسب نتيجة المقارنة).
 هذا البرنامج سيتصرف بالضبط مثل البرنامج السابق، لكن يمكننا أن نغيره كما نشاء باستخدام العامل الجديد! مثلاً من أجل الترتيب التنازلي، نقدم دالة مقارنة بالعلامة معكوسة λ س، ص : س < ص  .
ماذا لو أردنا ترتيب مصفوفة من الأشخاص حسب السن مثلاً؟ (اعتبر لدينا فصيلة اسمها "شخص" بها بيان اسمه "سن"). وقتها يكفي أن نقول هذا:

رتب (الأشخاص، λ أ، ب : سن أ > سن ب)


ماذا لو أردنا ترتيب الأشخاص حسب السن تنازلياً؟ يكفي تغيير العلامة في تعبير لامدا.

السبت، 24 نوفمبر 2012

تعبيرات لامدا في كلمات

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

إجراء ترحيب( ):
    اطبع "مرحباً بك" 
نهاية 

دالة مجموع (أ ، ب):
    ارجع ب: أ + ب 
نهاية 

س = الإجراء ترحيب 
ص = الدالة مجموع

سوف تجد في المتغير س كائناً object يسمح لي باستدعاء الإجراء 'ترحيب' متى أردت. وكذلك ص يحمل وسيلة لاستدعاء الدالة 'مجموع' استدعيهما هكذا:
س : تنفيذها ( ) 
ب = ص : تنفيذها (5، 10)

السطر الأول يطبع الترحيب على الشاشة، والسطر الثاني يضع القيمة 15 في المتغير ب.

لماذا قلنا "س: تنفيذها( ) " في أول سطر بينما قلنا "ب = ص: تنفيذها(...)" في الثاني؟ لأن س تعبر عن إجراء بينما ص تعبر عن دالة.

الآن ما فائدة هذا الاسلوب في إيجاد قيم تعبر عن الإجراءات والدوال؟ هيا نضرب مثلاً ظريفاً:

دالة كلهم (المصفوفة ، الاختبار) :
    لكل أ من 1 إلى عدد (المصفوفة) :
        إذا ليس الاختبار : تنفيذها (المصفوفة [أ]) :
            ارجع ب: خطأ 
        تم 
    تابع 
    ارجع ب: صحيح 
نهاية 

دالة زوجي (ر) :
    ارجع ب: باقي.قسمة (ر، 2) = 0 
نهاية 

م = [2 ، 8 ، 6 ، 12] 

اطبع كلهم (م، الدالة زوجي)

ماذا فعلنا؟ الدالة كلهم تأخذ مصفوفة ودالة اخرى اختبارية (تعود بالقيمة صحيح أو خطأ)، وتقوم بتطبيق الدالة على كل قيمة في المصفوفة، بحيث لو عادت تلك الدالة بـصحيح لكل القيم، تعود الدالة الأم أيضاً بـصحيح، وإلا عادت بـخطأ.

والآن لم تعد تحتاج كتابة هذا الـ loop مرة أخرى للتأكد أن شرطاً ما ينطبق على كل القيم في مصفوفة!

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

لحظة...سطر واحد؟ ألم أضطر لكتابة الدالة زوجي في المثال السابق؟ هذا يجعل الموضوع يأخذ أكثر من سطر واحد للأسف :(

حسناً، في الواقع لم أكن أحتاج لذلك، لأن كلمات بها أيضاً تعبيرات لامدا (lambda expressions)، وهي طريقة لكي أعرف دالة بلا اسم في المكان الذي أريد فيه قيمة تعبر عن دالة.

أي أنه بدلا من "اطبع كلهم(م، الدالة زوجي)" كان يمكن أن أقول:

اطبع كلهم (م، λ أ : باقي.قسمة (أ، 2) = 0)

هذه هي صيغة الـ Lambda expression :

λ متغيرات:  تعبير ينتج منه قيمة

ومعناها "الدالة التي تأخذ هذه المتغيرات كعوامل وترجع بقيمة هذا التعبير". مثلاً λ س: س+1 هي الدالة التي لو أخذت س تعود بـ س+1.

(لكي تكتب علامة لامدا في محرر كلمات اضغط زر Ctrl + L، لو كنت تستخدم محرراً آخر يمكنك إدخال رمز ^ بدلاً من لامدا)

في مثال "كلهم" قد مررت للدالة "كلهم" دالة أخرى، هي "الدالة التي لو أخذت أ تعود بالقيمة باقي.قسمة(أ،2)=0"، وهو نفس الدور الذي تقوم به الدالة زوجي -- التي لم نعد نحتاج إليها.

هل تريد أن تتأكد أن المصفوفة م لا تحتوي صفراً؟ بسيطة:

اطبع كلهم.ليسوا ( م ، λ أ : أ = 0 )

نحن هكذا نحاول أن ندخل بكلمات إلى عصر جديد: عصر الـfunctional programming.

أليس هذا صعباً على الأطفال؟ من يدري؟ نحن لم نجرب بعد. وعلى كل حال إن ظهر أن تلك الإمكانية صعبة فلا بأس: يمكننا أن نتركها في اللغة لكن لا نلزم الأطفال بتعلمها، وباقي اللغة متاحة لهم كما هي. ومن أراد من الكبار (أو الأطفال القادرين) أن يتعلمها فهي موجودة.

الدوال التي تأخذ دوالاً أخرى كعوامل تسمى higher order functions. مثال آخر لمثل هذه الدوال هو الدالة تناظر:

م = [ 1 ، 2 ، 3 ، 4 ] 
اطبع تناظر ( م ، λ أ : أ × 2 )

هذه الكود سوف تطبع على الشاشة [2، 4، 6، 8] لأن تناظر تأخذ مصفوفة ودالة، وتعود بمصفوفة أخرى مليئة بقيم المصفوفة الأولى بعد تطبيق الدالة على كل منها. أي أن كل قيمة في المصفوفة القديمة لها قيمة مناظرة لها في المصفوفة الجديدة.

مثال آخر هو الدالة اختزال :
م = [ 1 ، 2 ، 3 ، 4 ] 
اطبع اختزال ( م ، λ أ ، ب : أ + ب )

الكود السابقة سوف تطبع على الشاشة القيمة 10، لأن اختزال تأخذ مصفوفة ودالة، ثم تقوم بطبيق الدالة "بين العناصر" كأني في المثال السابق حسبت 1+2+3+4. طبعاً كان يمكنني أن أقدم دالة ضرب بدلاً من الجمع لأحسب حاصل ضرب الأعداد في المصفوفة) أو دالة max لأحسب أكبر قيمة في المصفوفة..أو...أو...

الأسم "تناظر" هو ما أطلقته - في محاولة للتعريب - على الدالة المعروفة بإسم map، والدالة "اختزال" هي reduce.

ربما تكون قد سمعت عن MapReduce. إنها تكنولوجياً مبني عليها كثير من التطبيقات في شركات مثل:
  • Google
  •  Yahoo
  • Microsoft
  • Facebook
    ...وغيرهم. الـfunctional programming ليس كلاماً رياضياً نظرياً. إنه مهم للتكنولوجيا والتطوير.

    هل تريد أن نبني جوجل خاصة بنا في مجتمعنا؟ لغة كلمات بها البذور العلمية لذلك لغرسها في الأطفال.

    الأحد، 18 نوفمبر 2012

    عن كون كلمات لغة للأطفال، ولماذا لا تكون لغة احترافية

    هناك أكثر من شخص تحدث معي عن سبب إصراري أن تكون لغة كلمات لغة تعليمية وليست لغة لعمل برامج احترافية مثلها مثل Java, Python, ...الخ.

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

    من الأسباب التي تدعو لجعل كلمات لغة احترافية:

    1. قد يكون هناك عائقاً نفسياً يمنع البعض من استخدام لغة للأطفال أو المبتدئين.
    2. ماذا سيفعل الأطفال (أو الكبار المبتدئين) بعد تعلم كلمات إن أرادوا عمل برامج احترافية؟
    3. ماذا عن حلم البرمجة باللغة العربية في كل مكان؟

    1- لن يحب أحد أن يستخدم لغة للمبتدئين: هذا جانب اجتماعي/تسويقي. هناك فئة من المبرمجين لا تحب إلا ما هو "قوي" و"احترافي". وهذا قد يدفع البعض لعدم استخدام كلمات لأنها "لعبة". هناك جانب آخر أجتماعي هو أن بعض العرب للأسف لا يرى أنه هناك منتج جيد يمكن أن يخرج من عقل مبرمج عربي، وبعض من هؤلاء سيظنون أنني جعلت كلمات للأطفال لأخفي عيوبها أو ليكون لدي عذر جاهز لأي ثغرة في اللغة: أنها للاطفال.

    2- ماذا سيفعل الأطفال (أو الكبار المبتدئين) بعد تعلمها؟ هذا سؤال معقول. هب أن شخصاً تعلم كلمات ثم أراد أن يصنع برامجاً كبيرة أو تجارية، هل سيجب عليه أن يتعلم لغة برمجة أجنبية؟ إن كنت أقول أنني صنعت كلمات لرفع حاجز اللغة الذي يعوق تعلم البرمجة، فلماذا لا أرفع أيضاً (أو بتعبير أدق: أساعد في رفع) حاجز اللغة الذي يعوق البرمجة الاحترافية؟

    3- ماذا عن تحقيق حلم البرمجة باللغة العربية؟ هذا جانب اجتماعي أيضاً: لو ظهرت لغة برمجة عربية احترافية، ألن يؤدي هذا لشعور بالفخر في سائر أنحاء الوطن العربي، وأن يزداد العرب ثقة في قدرتهم على النهوض بأمتهم، وأن يكون شيئاً جميلاً على العموم؟

    الرأي الخاص بي: لماذا لم أجعلها كذلك

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

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

    وهذا ما لا أستطيع أن أقوم به حالياً. ما أستطيع أن أقوم به هو تطوير اللغة نفسها، وهو ليس أمراً سهلاً حتى للغة مبتدئين.

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

    الجزء الأخير: الخطوات المطلوبة لتكون كلمات احترافية

    (الحديث هنا تكنولوجي وليس اجتماعي، لن أتحدث عن التسويق أو الدعم الفني...الخ ولكن عن تطوير اللغة نفسها).

    أولاً: كلمات في صورتها الحالية لغة قوية جداً. فيها مثلاً هذه الإمكانيات:
    • tail call elimination
    • destructuring
    • lambda expressions - في الإصدارة القادمة
    •  green threads
    • CSP channels
    وهي إمكانيات معظمها لا يوجد في ++C، ولا بايثون، ولا جافا. (في حالات معينة مثل CSP  يمكن تطعيم تلك اللغات بمكتبات خارجية لتقديم هذه الإمكانيات، لكن في حالات مثل tail calls لا يمكن).

    لكن على الجانب الآخر، اللغات الأخرى تقدم تنفيذا سريعاً للبرامج، تقدم garbage collection متقدم عن كلمات (ماعدا ++C التي لا تحب مثل هذه الرفاهيات)، تقدم مكتبات لأي شيء تريده، تقدم ضماناً معقولاً لخلو المترجم والآلة الافتراضية من الأخطاء، وتقدم خاصية استدعاء دوال خارجية [بايثون تقدم ctypes لاستدعاء إجراءات مكتوبة بالسي، جافا تقدم JNI لنفس السبب، سي شارب تقدم P/Invoke، وهكذا).

    كلمات تحتاج إذاً، لكي تكون أقرب للاحترافية:
    • تطوير الآلة الافتراضية الخاصة بها (وهي من تصميمي واسمها SmallVM) لتكون أسرع وبجامع مهملات أفضل. أو كتابة نسخة من كلمات تعمل على ألة افتراضية موجودة.
    • ضبط إمكانية FFI الخاصة بها (وهي الخاصية التي تكافيء JNI/ctypes المذكورة بأعلاه). الخاصية موجودة بالفعل في كلمات لكنها تحتاج لاستكمالها.
    • إصلاح الكثير من الثغرات والنواقص الموجودة
    • صنع خاصية multiplexing over threads، ولو تم صنع هذه الخاصية فستكون ميزة نادرة لكلمات، لا توجد إلا في لغات مثل Google Go أو Erlang
    • صنع بعض المكتبات الأساسية مثل web, database, networking
    • عمل كل هذا بدون التأثير على سهولة تعلم اللغة، أو ملاءمتها للأطفال، أو جمالها [ربما يتطلب هذا فصل اللغة إلى لغتين واحدة للاطفال والأخرى احترافية، لكن بنفس الـsyntax تقريباً ونفس المكتبات].
    بعض هذه الإمكانيات يجري العمل فيه فعلاً (مثلاً أعمل حالياً في خاصية multiplexing)، والبعض مؤجل للمدى الطويل، والبعض لا أدري إن كنت سأقوم به فعلاً أم لا. من يعلم؟ هؤلاء الذين يطلبون لغة احترافية، ربما يحصلون عليها ذات يوم.

    الجمعة، 16 نوفمبر 2012

    Solving an ACM problem with Kalimat (part 2)

    • هذا المقال تكملة للجزء الأول الذي تجده هنا، لكن في الواقع الجزء الثاني يتضمن كل المعلومات المطلوبة ولا تحتاج لقراءة الجزء الأول لتفهمه.
    • مسألة الـACM نفسها هنا
     ما المطلوب من المسألة؟ كتابة برنامج به وصف لمجموعة من المبان، وحذف الخطوط المتقاطعة بحيث يظهر فقط المحيط العام لتلك المبان، كما توضح الصورة:


    كيف نحل هذه المسألة؟ من حسن الحظ أن شرح السؤال نفسه يلمح لنا بالحل!
    The skyline vector should represent the "path" taken, for example, by a bug starting at the minimum x-coordinate and traveling horizontally and vertically over all the lines that define the skyline.

    يقول لنا البرنامج أن الحل يصف تحركات حشرة تتحرك عبر صف المبان. تعال نتخيل أن هذه الحشرة هي نملة (بدلاً من البقة المذكورة في المسألة). الحركة في الصور التالية من اليسار لليمين:

    في البداية ستسير النملة أفقياً حتى تصطدم بجدار بمبنى:
    فإن وجدت المبنى ستبدأ في التحرك رأسياً لتتسلقه


    إن وجدت نفسها عند سفح مبنى من جديد، فستبدأ في تسلقه، وإن وجدت نفسها عند هضبة فستنزل

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

    وكيف تنتقل بين الحالات؟

    إن كانت واقفة على الأرض:
    • تسير إلى سفح أول مبنى أمامها

    إن كانت واقفة على سطح مبنى:

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


    إن كانت عند سفح:
    • تصعد لأعلى المبنى، ثم تكون حالتها "على سطح مبنى"
    إن كانت عند هضبة:
    • إن كان هناك سطح مبنى يعوق هبوطها، تنزل إلى سطح ذلك المبنى ثم تصبح حالتها "على سطح مبنى"
    • إن لم يكن هناك عائق تنزل إلى الأرض (ص = 0) وتصبح حالتها "على الأرض"



    الملخص:

    لم يبق سوى كتابة الكود التي تمثل هذا الرسم، ثم نستطيع جميعنا أن نعود إلى البيت ونأكل الساندوتشات التي أعدتها أمهاتنا :)


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

    الآن إلى الكوووود!!


    كما نعلم، مجموعة البيانات المدخلة هي مصفوفة من ثلاثيات، مثل [[12، 10، 20]، [15، 8، 22]، [20، 12، 30]]، وكل ثلاثية تعبر عن مبنى [الحافة اليسرى، الارتفاع، الحافة اليمنى]. فلنبدأ بشيء بسيط: دوال قصيرة لاستخراج بيانات كل مبنى مما يجعل البرنامج أسهل في القراءة:
    دالة يسار ( مبنى ) :
        ارجع ب: مبنى [ 1 ] 
    نهاية 
    
    دالة سطح ( مبنى ) :
        ارجع ب: مبنى [ 2 ] 
    نهاية 
    
    دالة يمين ( مبنى ) :
        ارجع ب: مبنى [ 3 ] 
    نهاية

    الآن نرى الإجراء الأساسي لحل المسألة:
    بداية.البحث مشترك 
    
    إجراء إجابة ( صف.المباني ) :
        بداية.البحث = 1 
        على.الأرض ( 0 ، 0 ، صف.المباني ) 
    نهاية 

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

    ماذا أخذ الإجراء على.الأرض؟ أخذ قيم س، ص، ومصفوفة المباني.

    جميل جداً. الآن ننظر للإجراء نفسه:

    إجراء على.الأرض(س ، ص ، المباني) :
     
     لكل أ من بداية.البحث إلى عدد(المباني) :
            إذا يسار(المباني [أ]) > س :
                س = يسار(المباني[أ]) 
                اطبع س 
                حدث.بداية.البحث(س، المباني) 
                وكل إلى عند.سفح(س، ص، المباني[أ] ، المباني) 
            تم 
        تابع 
             -- إن وصل التنفيذ هنا فمعنى ذلك
             -- أن النملة على الأرض ولا يوجد
             -- مبان على يمينها: أي أن الرحلة
             -- قد انتهت
     نهاية

    الإجراء مكون من جزئين: الجزء الأول (بين لكل...تابع) يبحث عن أول مبنى على يمين النملة، فإن وجده:
    - يغير قيمة "س" ليحرك النملة عند سفح المبنى، ويطبع القيمة الجديدة كما هو مطلوب في المخرجات
    - ينادي حدث.بداية.البحث(س، المباني) لكي يبحث عن أول مبنى على يمين س الجديدة ويجعل رقم هذا المبنى هو بداية.البحث
    - ينفذ السطر وكل إلى عند.سفح (____ ) ويعطيه بعض القيم، مما يجعل البرنامج الآن في حالة جديدة.

    ما معنى وكل إلى؟ انها تجعل السطر من "استدعاء إجراء" إلى "توكيل لإجراء". ما الفرق بينهما؟ في حالة التوكيل فإنه قبل استدعاء عند.سفح(...) سوف يتم أولاً حذف الstack frame الخاصة بـ على.الأرض( ) من الذاكرة، وهذا  طبيعي لأن هذا الجزء من البرنامج لا نحتاج أن نرجع إليه.

    الآن نرى عند.سفح:
    إجراء عند.سفح ( س ، ص ، مبنى ، المباني ) :
    
        ص = سطح ( مبنى ) 
        اطبع ص 
        وكل إلى عند.سطح ( س ، ص ، مبنى ، المباني ) 
    نهاية 

    إذا كانت النملة عند سفح مبنى، فإنها تتسلقه (بتغيير قيمة ص وطباعتها)، ثم توكل سائر العمل إلى الحالة الجديدة: عند.سطح

    سهلة دي مش كدة؟

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

    لا تنزعج! الكود أبسط مما يبدو عليها. هي ببساطة تقوم بالآتي:
    1- نفذ لكل مبنى م في المباني التي على يمين النملة:
    • لو كان يسار م أكبر من يمين المبنى الذي نقف عليه، إذا فكل المباني بعيدة ولا توجد عقبات. سوف نذهب للخطوة 2
    • لو كان الأمر غير هذا، فقد وجدنا مبنى يقطع مبنانا! لو كان أعلى من مبنانا أيضاً فيجب أن نصل إلى سفحه. حرك س إلى سفح المبنى، واطبعها، وانتقل لحالة عند.سفح
    2- لو انتهت الحلقة ولم نجد عقبة، إذاً فنحن على سطح مبنى بلا عقبات. هيا نسير إلى نهاية السطح (نغير س ونطبعها)، ثم ننتقل إلى حالة عند.هضبة

    لو كنت لاتزال تجد صعوبة، انظر إلى الصور مرة أخرى.

    الآن نصل إلى عند.هضبة:
    إجراء عند.هضبة(س، ص، مبنى، المباني) :
    
     أعلى.عقبة = لاشيء 
        لكل أ من 1 إلى عدد(المباني) :
            م = المباني[أ] 
            إذا م <> مبنى وأيضا يمين(م) > س وأيضا يسار(م) < س :
                 -- لقد وجدنا مبنى يقطع مبنانا
                 -- وهو بالتأكيد ليس أعلى منه لأنه لو كان أعلى
                 -- لكنا عليه بدلا من المبنى الحالي
     
                 -- لو كنا مازلنا في بداية البحث، نعطي
                 -- قيمة مبدئية للمتغير أعلى.عقبة
                إذا أعلى.عقبة = لاشيء :
                    أعلى.عقبة = م 
                تم 
                 
                 -- سوف نختار أعلى مبنى فيهم لنهبط إليه
                إذا سطح(م) > سطح(أعلى.عقبة):
                    أعلى.عقبة = م
                تم 
            تم 
        تابع 
        إذا أعلى.عقبة = لاشيء :
            ص = 0 
            اطبع ص 
            وكل إلى على.الأرض(س، ص، المباني) 
        وإلا :
            ص = سطح(أعلى.عقبة) 
            اطبع ص 
            وكل إلى عند.سطح(س، ص، أعلى.عقبة، المباني) 
        تم 
    نهاية

    مرة أخرى: الأمر أبسط مما يبدو عليه. الدالة جزءان:

    الجزء الأول بين "لكل...تابع" يبحث عن أعلى مبنى يحتوي النقطة س التي تقف فيها النملة (يمينه أكبر من س ويساره اقل منها). لأن هذا الشرط ينطبق على المبني الحالي الذي تقف النملة عليه؛ لابد أن نضع جزءاً من الشرط يتأكد أن م هو مبنى مختلف عن المبنى الحالي.

    الجزء الأول بين "لكل...تابع" له دور آخر: من بين كل المباني التي تحتوي س، فإنه يختار أعلاها. هذا يتم عن طريق ثلاثة أجزاء:
    • خارج الـloop، يجعل قيمة المتغير أعلى.عقبة تساوي لاشيء.
    • داخل الـloop، يعطي هذا المتغير قيمة أولية هي أول مبنى يصلح يجده، وذلك عن طريق الشرط إذا أعلى.عقبة = لاشيء :... هذا الجزء يتم تنفيذه مرة واحدة فقط.
    • داخل الـloop أيضاً، يتم كل مرة مقارنة م بأعلى عقبة، لحساب قيمة max في المتغير أعلى.عقبة بالطريقة المعروفة.
    الجزء الثاني: لو كانت قيمة المتغير أعلى.عقبة تساوي لاشيء، فمعنى هذا أنه لا توجد عقبات، فتنزل النملة على الأرض (تغير ص وتغير حالتها).

    لو كان هناك قيمة للمتغير أعلى.عقبة، فهناك إذاً عقبة أمام نزولنا. بدلاً من النزول للأرض ننزل إلى سطح تلك العقبة (المبنى) ونغير الحالة.

    أخيراً هذا هو الإجراء حدث.بداية.البحث
    إجراء حدث.بداية.البحث (س، المباني) :
        
        لكل أ من بداية.البحث إلى عدد( المباني ) :
            إذا يسار (المباني [أ]) > س :
                بداية.البحث = أ 
                اذهب إلى النهاية 
            تم 
        تابع 
        علامة النهاية 
    نهاية

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

    هل يعمل البرنامج؟ القيم المطبوعة هي نفس النتيجة المكتوبة في موقع المسألة (ولكني لم أختبره على مدخلات أخرى)
    يمكنك أن تختبره أنت إن أردت! الكود كاملةً تجدها هنا:
    -- هذا البرنامج حل لمسألة Skyline problem من مسائل الـACM
     -- تجد المسألة هنا: http://uva.onlinejudge.org/external/1/105.html
     
    -- اختبارات()
     حل.المسألة ( ) 
    
    إجراء حل.المسألة ( ) :
        صف.المباني = [ ] 
        ملف.المدخلات = افتح.ملف ( "input.txt" ) 
        كرر مادام ليس ملف.المدخلات : منته ( ) :
            س = ملف.المدخلات : اقرأ.سطر ( ) 
            الثلاثية = تفصيص ( س ، " " ) 
            الثلاثية [ 1 ] = كعدد ( الثلاثية [ 1 ] ) 
            الثلاثية [ 2 ] = كعدد ( الثلاثية [ 2 ] ) 
            الثلاثية [ 3 ] = كعدد ( الثلاثية [ 3 ] ) 
            صف.المباني = صف.المباني + [ الثلاثية ] 
        تابع 
        اغلق.ملف ( ملف.المدخلات ) 
        إجابة ( صف.المباني ) 
    نهاية 
    
    
    بداية.البحث مشترك 
    
    إجراء إجابة ( صف.المباني ) :
        بداية.البحث = 1 
        على.الأرض ( 0 ، 0 ، صف.المباني ) 
    نهاية 
    
    
    بداية.البحث مشترك 
    إجراء على.الأرض ( س ، ص ، المباني ) :
    
     لكل أ من بداية.البحث إلى عدد ( المباني ) :
            إذا يسار ( المباني [ أ ] ) > س :
                س = يسار ( المباني [ أ ] ) 
                اطبع س 
                حدث.بداية.البحث ( س ، المباني ) 
                وكل إلى عند.سفح ( س ، ص ، المباني [ أ ] ، المباني ) 
            تم 
        تابع 
             -- إن وصل التنفيذ هنا فمعنى ذلك
             -- أن النملة على الأرض ولا يوجد
             -- مبان على يمينها: أي أن الرحلة
             -- قد انتهت
     
    نهاية 
    إجراء عند.سفح ( س ، ص ، مبنى ، المباني ) :
    
     ص = سطح ( مبنى ) 
        اطبع ص 
        وكل إلى عند.سطح ( س ، ص ، مبنى ، المباني ) 
    نهاية 
    
    إجراء عند.سطح ( س ، ص ، مبنى ، المباني ) :
          --  اطبع "سطح"
     لكل أ من بداية.البحث إلى عدد ( المباني ) :
            م = المباني [ أ ] 
             -- لو وجدنا مبنى خارجاً تماماً عن حدود المبنى الحالي،
             -- فكل المباني التي تليه هي الأخرى خارجة عن الحدود
             -- لاحظ أن الأمر التالي غير مختوم بكلمة "تم" بل
             -- الأمر كله سطر واحد
             إذا يسار ( م ) > يمين ( مبنى ) : اذهب إلى نهاية.البحث 
            
             -- هل م أعلى من المبنى الذي تقف عليه النملة؟
             إذا سطح ( م ) > سطح ( مبنى ) :
                 -- في هذه الحالة فهو عقبة. هيا نتحرك إلى سفحه
                 س = يسار ( م ) 
                 اطبع س 
                 حدث.بداية.البحث ( س ، المباني ) 
                وكل إلى عند.سفح ( س ، ص ، م ، المباني )        
             تم 
        تابع 
        علامة نهاية.البحث 
         -- إن وصلنا هنا ولم نجد شيئاً، نسير إلى نهاية المبنى الحالي
          س = يمين ( مبنى ) 
        اطبع س 
        حدث.بداية.البحث ( س ، المباني ) 
        وكل إلى عند.هضبة ( س ، ص ، مبنى ، المباني ) 
    نهاية 
    
    إجراء عند.هضبة ( س ، ص ، مبنى ، المباني ) :
    
        أعلى.عقبة = لاشيء 
        لكل أ من 1 إلى عدد ( المباني ) :
            م = المباني [ أ ] 
            إذا م <> مبنى وأيضا يمين ( م ) > س وأيضا يسار ( م ) < س :
                 -- لقد وجدنا مبنى مختلف يقطع مبنانا
                 -- وهو بالتأكيد ليس أعلى منه لأنه لو كان أعلى
                 -- لكنا عليه بدلا من المبنى الحالي
     
                 -- لو كنا مازلنا في بداية البحث، نعطي
                 -- قيمة مبدئية للمتغير أعلى.عقبة
                 إذا أعلى.عقبة = لاشيء :
                    أعلى.عقبة = م 
                تم 
                عقبة2 = م 
                 -- سوف نختار أعلى مبنى فيهم لنهبط إليه
                إذا سطح ( عقبة2 ) > سطح ( أعلى.عقبة ) :
                    أعلى.عقبة = عقبة2 
                تم 
            تم 
        تابع 
        إذا أعلى.عقبة = لاشيء :
            ص = 0 
            اطبع ص 
            وكل إلى على.الأرض ( س ، ص ، المباني ) 
        وإلا :
            ص = سطح ( أعلى.عقبة ) 
            اطبع ص 
            وكل إلى عند.سطح ( س ، ص ، أعلى.عقبة ، المباني ) 
        تم 
    نهاية 
    
    إجراء حدث.بداية.البحث ( س ، المباني ) :
        
        لكل أ من بداية.البحث إلى عدد ( المباني ) :
            إذا يسار ( المباني [ أ ] ) > س :
                بداية.البحث = أ 
                اذهب إلى النهاية 
            تم 
        تابع 
        علامة النهاية 
    نهاية 
    
    
    دالة يسار ( مبنى ) :
        ارجع ب: مبنى [ 1 ] 
    نهاية 
    
    دالة سطح ( مبنى ) :
        ارجع ب: مبنى [ 2 ] 
    نهاية 
    
    دالة يمين ( مبنى ) :
        ارجع ب: مبنى [ 3 ] 
    نهاية

    الخميس، 15 نوفمبر 2012

    Solving an ACM problem with Kalimat

    هذه هي المسألة التي يتحدث عنها المقال، اقرأها جيداً ثم تعال وأكمل القراءة :)

    شكل مبدئي للحل
     
    البرنامج يقرأ ملفاً به عدد من السطور، كل سطر هو مجموعة من الأرقام تفصل بينها مسافات، وكل ثلاثة أرقام متتالية تعبر عن مبنى في المدن التي تتعامل معها المسألة. إذاً فلنبدأ بقراءة ملف المدخلات وتقسيم كل سطر إلى ثلاثيات
    إجراء حل.المسألة() :
        صف.المباني = [ ] 
        ملف.المدخلات = افتح.ملف("input.txt") 
        كرر مادام ليس ملف.المدخلات : منته() :
            س = ملف.المدخلات : اقرأ.سطر() 
            الثلاثية = تفصيص(س ، " ") 
            صف.المباني = صف.المباني + [الثلاثية] 
        تابع 
        اغلق.ملف(ملف.المدخلات) 
        اطبع إجابة(صف.المباني) 
    نهاية

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

    دالة إجابة(صف.المباني) :
        -- مؤقتا  سوف نرجع بالمدخلات كما هي
        -- للتأكد أن الخطة العامة صحيحة
        ارجع ب: صف.المباني 
    نهاية

    عظيم! يمكننا الآن تشغيل البرنامج (بعد التأكد من وجود ملف input.txt به مثال للمدخلات الصحيحة كما في صفحة المسألة).

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

    هل ينفع هذا؟ يبدو أن كلام المشككين صحيح: العرب لا يستطيعون عمل لغات برمجة. لماذا لا يرسل أحدكم رسالة بريدية غاضبة لمصمم اللغة يؤنبه على إهماله؟ :(

    القصد، قمت بتصحيح الخطأ وإعادة تنفيذ البرنامج:




    حسناً! نحن نقرأ المدخلات بصورة صحيحة، ويبقى أن نحل المسألة. هل سنقدر على هذا؟ وسؤال آخر: هل سيعمل الحل بسرعة؟ لاحظ أن كلمات أبطأ بكثير من لغة مثل سي++. هذه لغة مصممة للتعليم والرسم بالكمبيوتر والألعاب، وليس لـ"طحن الأرقام". أنا لم أحل المسألة بعد، لذلك فأنا لا أعرف فعلاً إجابة السؤال. سأبدأ الآن في حلها..

    اضغط هنا لتقرأ الجزء الثاني

    الجمعة، 9 نوفمبر 2012

    التاريخ والأشياء الصغيرة

    أهوى علم لغات البرمجة (علم مختلف عن الـcompilers). وأفكر دائماً في لغة البرمجة "النموذجية" التي يمكن التعبير بها عما تريد بدون فجوة بين الفكرة في دماغك والكود التي تكتبها. يتفرع من هذا فكرة لغة البرمجة التعليمية التي يمكن لأي شخص أن يدخل منها عالم البرمجة، ولغة البرمجة "الديموقراطية" التي تمكّن أي شخص (صحفي، محامي) أن يكتب برنامجاً في مجال تخصصه، وهكذا.

    وأقوى سلاح في هذه الرحلة هو التاريخ. وهكذا تجدني أقضي الساعات في القراءة عن لغات البرمجة القديمة منذ الستينات وما بعدها.

    Algol - Icon -  CLU - Simula - Self - Forth - Logo - CleanSheet - ObjectVision

    ..بحثاً عن فكرة عبقرية لكن مفقودة.



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

    وفي أيام معركة الجمال في الثورة، ابتكر الثوار - وحدهم - طرقاً حربية مشابهة لما كان يفعله الرومان. حائط بشري يمسك دروع طويلة ويزحف بها... Testudo formation

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

    تلك المخطوطات منذ أيام الحضارة الإسلامة فيها أسلوب معين لكتابة الكتب: العنوان الطويل لكل فقرة الذي يصلح ملخصاً للفقرة. هناك كاتب أجنبي استخدم هذا الاسلوب في كتابه للذكاء الاصطناعي (الكاتب Patrick Henry Winston)، واستخدمته أحياناً في مقالات على المدونة. في الواقع الكتب الإسلامية القديمة كان بها ميزة ظريفة جداً هي أن الكاتب كان يصنع الكتاب خليطاً من الكتابة والرسم ينسابان مع بعضهما في حرية. كان الكتاب عملاً فنياً وليس مجرد وسيلة تقديم معلومات، وربما نحتاج أن تكون المزيد من الكتب بهذه الطريقة.

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

    كتب التاريخ الأجنبية تهتم كثيراً بالحياة: ماذا كان شكل المنازل في تلك الحقبة التاريخية؟ كيف كان الناس يطبخون؟ ماذا كانت الوظائف وقتها؟ التاريخ الذي أخذته في المدرسة المصرية كان في أغلبه سلسلة من الحكام والمعارك.

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

    الخميس، 8 نوفمبر 2012

    A notation for a sketch-based programming language

    This is my design for a language called "SketchCode". The language tries to enable sketch-based programming, both on a traditional paper pad or on a tablet-like device, and strives to maintain simplicity and clarity of the sketches by reusing whatever notations people already use when sketching and by introducing as few new notations as possible.

    Instead of making the code itself graphical, the system keeps the code mostly text based while using graphical notation for data structures. Blocks of simple shapes indicate objects, arrows indicate object references, tables indicate records or arrays.

    As you see, this simple scheme already allows us to represent many kinds of data. In SketchCode our program should be mostly graphs, state machines, ...etc, I like the idea that a program's data stands out more than its code.  As Fred Brooks said: Show me your flowchart and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won't usually need your flowchart; it'll be obvious

    The graphical nature of the data allows us to program by pattern matching as seen in a lot of functional languages. An important set of new notations would be the language of pattern matching: how to indicate matching a variable? How to match a part of an array or structure?



    The first example shows how to calculate the sum the elements in a (sketched) linked list, the second shows how to extract data from a record, and the third shows the notation for 'if' statements.

    Tentatively, I chose to underline an identifier in a pattern to indicate being a variable, and the "piece of a table" to indicate partial record matching. It would be interesting to see how to indicate other notations like a part of a graph or a slice of an array.

    I have also borrowed some notations from math, and an implemented system could add traditional notations for things like roots, fractions, summation,...etc

    A lot can be done with just those basic components: we could have unit tests with sketches of test input, or a "console" where we sketch function application to evaluate it, and more. From there, there is so much that could be added.

    Of course, such a system needs to be implemented in order to find out whether this design is really sufficient to express interesting programs, and whether such expressions are actually natural to draw/understand, and how to e.g naturally modify an existing program.

    السبت، 3 نوفمبر 2012

    عطش للأفكار الأصيلة

    حين أقول "فكرة أصيلة" فقولي محاولة لترجمة original idea، والمقصود هو فكرة ليست مجرد تكرار للأفكار المعروفة. وأنا أعيش في حالة عطش دائم للأفكار الأصيلة، ويمكن تلخيص حياتي في أنها رحلة بحث عن الجديد، والنادر، والمبتكر. ولذلك أتحدث عن البرمجة، والرسم، والتأليف. كل من هذه هو ببساطة رحلة بحث عن فكرة جديدة [وتنفيذها].

    والمشكلة هي أنني شخص يحب الأفكار الأصيلة يعيش في عالم يحب المكرر والمألوف...

    - الشباب الجامعي الذي "يمزح" عن طريق تكرار القفشات من الأفلام الكوميدية.
     - السياسيون الذين يكررون الشعارات كل يوم حتى صارت تصريحاتهم هي clip show من لصق الشعارات ببعضها (والجمهور يحب هذا وينتخبهم).
     - حتى البرمجة؛ يكاد يصير تلخيصها الآن في تطبيقات "خزن البيانات ثم اعرضها"، والـUML ، والـ design patterns.

    هل تريد أكبر دليل على كلامي؟ انظر إلى لغة مثل الـ Prolog، إنها "فكرة أصيلة" بمعنى الكلمة، وتغير من تفكيرك في البرمجة تماماً، لكن الجميع يسخر منها لأنها أكاديمية، أو لأنه لم يفهم الcut، أو لأنها لن تأتي له بوظيفة. حتى الذين يحبونها، يحبونها بطريقة "لغة كويسة" وليس بطريقة "أريد أن أعرف المزيد!!".

    وفي عالم الشركات الجديدة وريادة الأعمال، توجد هذه الأيام مقولة منتشرة هي "الأفكار ليست مهمة، المهم التنفيذ"، وهذا تحوير ساذج لمقولة أقرب للدقة هي "مهما كانت الفكرة جيدة لن تنجح إن كان التنفيذ سيئاً".

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

    وحين بدأت في الكتابة على هذه المدونة، قررت شيئاً: لن أكتب في شيء إلا لو كان الموضوع نفسه جديداً، أو إن كان الموضوع معروفاً فلن أكتب إلا لو كان لديّ نظرة جديدة أو زاوية جديدة له. لن تجد على هذه المدونة مقالاً عن التنمية البشرية مثلاً، أو مقالاً يشرح الـquicksort. بشكل عام إن كان البحث بجوجل عن الموضوع يعطي مئات النتائج فلن أكتب عنه.

    ولن يكون للمدونة قراء كثيرون جداً :)