التفريغ الكامل للمحاضرة 1: مقدمة
التفريغ الكامل للمحاضرة 1: مقدمة
المصدر الأصلي: التفريغ الإنجليزي الرسمي بصيغة PDF، المرتبط بـصفحة المحاضرة وصفحة الفيديو.
النسبة والترخيص: آنا بيل (Ana Bell)، مقرر 6.100L: مقدمة في علوم الحاسوب والبرمجة باستخدام Python، خريف 2022، معهد ماساتشوستس للتكنولوجيا، MIT OpenCourseWare. هذه ترجمة وتكييف عربي غير رسمي، ولا يعني نشرها تأييد MIT أو اعتمادها منه. المواد الأصلية المشمولة والترجمة تحت رخصة CC BY-NC-SA 4.0.
منهج المطابقة: عناوين صفحات المصدر أدناه تطابق فواصل الصفحات في الاستخراج الرسمي، وعددها 16 صفحة غير فارغة. كل فقرة كلامية تقابل فقرة واحدة في المصدر، بما فيها أسئلة الحضور والتكرار والتصحيح الشفهي. وتبدأ كل صفحة في المصدر بصفحة عنوان الفيديو وثلاث علامات صوتية قصيرة، وقد أُهملت هنا لأنها لا تنقل معنى.
صفحة المصدر 1
آنا بيل: حسنًا، أهلًا بكم في المحاضرة الأولى من مقرر 6.100L. اسمي آنا بيل، وهذان اسمان منفصلان: الاسم الأول آنا، واسم العائلة بيل. وهذا أمر مربك حقًا. أنا محاضِرة في قسم الهندسة الكهربائية وعلوم الحاسوب هنا منذ نحو عشر سنوات، وأُدرّس المقرر التمهيدي منذ مدة. سعيدة حقًا بأن أُدرّس نسخة الفصل الكامل من 6.100A هذه السنة.
إذن ماذا سنفعل اليوم؟ سنمرّ أولًا سريعًا على بعض المعلومات الإدارية للمقرر، ثم نغوص مباشرة في التفكير حول الحاسوب: كيف يعمل على المستوى العالي؟ ثم نبدأ في أساسيات Python. بمعنى آخر، سنبدأ البرمجة فورًا. لذا أحثّك بشدة على تنزيل شرائح المحاضرة مسبقًا، وعلى تدوين الملاحظات، وعلى تشغيل الشيفرة عندما أفعل ذلك.
بعض المحاضرات تفاعلية. وستكون لدينا استراحات، أي فترات يسعك أن تأخذ فيها استراحة لتكتب شيفرة فعلًا. وهي مهمة؛ أسميها استراحات «جرّب بنفسك» (you try it). من المهم أن تتأكد أنك تتدرب عمليًا على ما نتعلمه في هذه اللحظة. الفكرة الأساسية هي أنني سأقوم بالتدريس، لكن ستكون هناك أيضًا فرص لطرح الأسئلة ولكم لتجربة البرمجة فورًا وعلى المباشر.
حتى لو لم تكملوا برنامجًا سنبدأ التحدث عنه، فسأكمله أنا، ويمكننا جميعًا التحدث عنه معًا. وسأريكم بعض الفخاخ وأشياء من هذا النوع. وستكون هناك فرص كثيرة للتدريب في هذا الصف بمستويات تفصيلية مختلفة، وفرص كثيرة في الـhandouts بين أيديكم للتدريب الإضافي في المنزل، ومن خلال مجموعة متنوعة من المصادر الأخرى.
أؤكد على المشاركة والتدريب لأن جزءًا من سبب وجودكم هنا هو أنكم تريدون تعلم البرمجة. إنكم لا تعرفون البرمجة بعد، والبرمجة في الحقيقة مهارة (Skill). إنها مثل الرياضيات أو القراءة؛ شيء عليك أن تتدرب عليه. لا يمكنك أن تكتفي بمشاهدة أنني أكتب لك سطورًا من الشيفرة، ثم حين يأتي وقت الاختبار تعرف تلقائيًا كيف تفعل ذلك. عليك أن تفعله كثيرًا، وبشكل متزايد، حتى يصبح من نوع البديهة تقريبًا.
إذن الأشياء الثلاثة الكبرى التي ستخرج بها من هذا الصف هي: معرفة المفاهيم (Knowledge of concepts)، وهي واضحة — نُركّز على بعض أفكار في علوم الحاسوب؛ ومهارة البرمجة (Programming skill)؛ ومهارات حل المسائل (Problem solving).
المحاضرات والامتحانات تساعدك أساسًا على اختبار معرفتك بالمفاهيم والحصول عليها. وتمارين الإصبع (Finger exercises) تعطيك مهارات البرمجة. ومجموعات المسائل (Problem sets) تساعدك على حل المسائل. فبشكل أساسي، إذا أُعطيت صيغة إنجليزية لمسألة، فكيف تنتقل من ذلك إلى التفكير: ما مفاهيم علوم الحاسوب التي يمكنني تطبيقها؟ وبعد ذلك، كيف آخذ تلك المفاهيم وأقوم بالبرمجة الفعلية؟
إذن ما الموضوعات التي سنغطيها؟ في جوهرها، سنتعلم التفكير الحوسوبي (Computational thinking). حين تصادف مسألة، لا ينبغي أن يكون تفكيرك الأول: كيف أحلّها رياضيًا؟ أو: كيف أحلّها بالقوة الغاشمة (Brute force)؟ بل: كيف يمكنني توظيف الحوسبة لمساعدتي في حل هذه المسألة؟
صفحة المصدر 2
وسنرى خلال هذه المحاضرات أمثلة على توظيف الحوسبة في مسألة ربما تكونون قد رأيتمها من قبل وحللتموها رياضيًا، وهذا رائع إلى حد ما. وبطبيعة الحال، للوصول إلى ذلك، سنتعلم لغة البرمجة Python. وبعد أن نتقن الأساسيات، سنرى كيف نبدأ في هكلة شيفرتنا لتبدو أفضل قليلًا، فلا يكون لدينا مجرد كومة شيفرة مدفونة في ملف. سنبدأ في تنظيم شيفرتنا ونرى كيف يمكننا أن نجعلها مرتّبة ومقروءة ووحدوية (Modular).
ثم لاحقًا — ليس في هذه المحاضرة، بل بعد محاضرتين، وكموضوع يمتد عبر هذا الصف — سننظر في بعض الخوارزميات (Algorithms). إنها ليست معقّدة حقًا، لكنها نوعًا ما الخوارزميات الأساسية لمجموعة كبيرة من الخوارزميات التي قد تراها في المستقبل إن قرّرت أن تدرس المزيد من مقررات علوم الحاسوب.
وأخيرًا، قرب نهاية الصف، سنرى التعقيد الخوارزمي (Algorithmic Complexity)، وهو ما يعني أننا سنبدأ في طرح السؤال: كيف نعرف أن البرامج التي نكتبها فعّالة؟ إذا كتبنا برامج، فكيف نعرف أنها سريعة؟ وكيف نعرف أنها لا تستهلك كل ذاكرة الحاسوب؟ هكذا، أي مقارنة خوارزميات مختلفة تفعل الشيء نفسه بعضها ببعض.
إذن إن لم تكن هناك أسئلة — وقلتُها من قبل، كثرٌ من هذه المعلومات موجودة أصلًا في الـhandout مع المزيد — يمكننا أن نبدأ. حسنًا، لنبدأ إذن بالتحدث عن المعرفة (Knowledge).
المعرفة التقريرية (Declarative knowledge) هي تقرير حقيقة، وكثير منا ربما تعامل معها في الرياضيات وفي الماضي. لكن هذه ليست الطريقة التي تعمل بها علوم الحاسوب؛ هذا ليس كيف يعمل هذا الصف. في علوم الحاسوب نتعامل مع المعرفة الأمرية (Imperative knowledge)، وهي في الأساس وصفة: كيف تفعل شيئًا ما. وحين نبرمج، كل ما نفعله هو كتابة وصفة للحاسوب ليفعل شيئًا ما. هذا كل شيء.
إذن إليك مثال عددي. التقرير الأول هو تقريري (declarative): «الجذر التربيعي للعدد x هو y بحيث إن y في y يساوي x». هناك قيم ممكنة كثيرة لـ x و y يمكن أن يتحقق فيها هذا التقرير. لكن إذا أعطينا هذا التقرير لحاسوب، فلن يعرف ماذا يفعل به. ما نحتاج إلى فعله هو أن نخبر الحاسوب كيف يجد الجذر التربيعي لعدد، ثم يخبرنا بما هو الجذر التربيعي لذلك العدد. إذن يحتاج الحاسوب إلى وصفة.
إذن الوصفة، وهي وصفة بسيطة جدًا لإيجاد الجذر التربيعي لعدد، هي: الخطوة الأولى، الثانية، الثالثة. ولنفقل إننا نريد إيجاد الجذر التربيعي للعدد 16. من الواضح أننا نعرف أنه أربعة، لكن الحاسوب لا يعرف. إذن نعطيه تخمينًا أوليًا. لنقل إن التخمين هو ثلاثة. كيف ننتقل من هناك؟
إذن الخطوات التي نتبعها: الخطوة الأولى، إذا كان 3 في 3 يساوي 9 قريبًا بما يكفي من 16، يمكننا التوقف. إنه ليس قريبًا حقًا بالنسبة لي. إذن لنواصل. الخطوة الثانية — بخلاف ذلك، سنصنع تخمينًا جديدًا بمعدل g، وهو تخميننا الأصلي 3، و x على g، وهو 16 على 3. كان 16 هو الجذر التربيعي الذي أردنا إيجاده. إذن تخميننا التالي هو 4.17. حسنًا، وباستخدام التخمين الجديد، نكرر العملية حتى نكون قريبين بما يكفي.
إذن نعود إلى الخطوة الأولى. نأخذ 4.17 في 4.17 فيحصل 17.36. إذن الآن نقول: هل هذا قريب بما يكفي؟ ليس حقًا. إذن لنفعلها من جديد. نصنع تخمينًا جديدًا بمعدل 4.17 و16 مقسومًا على 4.17. هذا يعطينا تخميننا الجديد، 4.0035.
صفحة المصدر 3
حسنًا، الخطوة التالية، وباستخدام التخمين الجديد، نكرر العملية. إذن 4.0035 في 4.0035 يساوي 16.0277، أي 0.0277. هل هذا قريب بما يكفي من x؟ نعم، يمكنني أن أكون راضيًا بهذا؛ فنحن ضمن زائد أو ناقص 1 تقريبًا. لكن إن أردنا أن يكون ضمن زائد أو ناقص 1 مضروبة في أُس 10 السالب 6 أو 7، فسنواصل العملية.
إذن ما كان لدينا هنا فعليًا هو خوارزمية (Algorithm). إنها تسلسل من الخطوات: الأولى، والثانية، والثالثة. وهناك نوع من تدفّق التحكم (Flow of control). كان لدينا موضع قلنا فيه: إذا كان التخمين قريبًا بما يكفي، يمكننا التوقف؛ بخلاف ذلك، نفعل شيئًا آخر. وكان لدينا تدفّق تحكّم آخر قلنا فيه: أعد هذا الشيء. إذن نحن لسنا نمشي خطيًا بل نغيّر التدفّق. وأخيرًا هناك طريقة للتوقف؛ نحن لا نريد للخوارزمية أن تستمر إلى الأبد، نودّ أن نتوقف عند نقطة ما.
وهذه النقطة التي نتوقف عندها، كنت غامضًا فيها نوعًا ما، لكنها قد تكون عندما نكون ضمن زائد أو ناقص 1 من الإجابة الفعلية. إذن الوصفات، في جوهرها، خوارزميات، صحيح؟ كانت جدتي تعلّمني الخوارزميات في الحقيقة عندما كانت تعلّني خَبز كعكة. لم تكن تسمّي ذل «خوارزميات»، لكنها كانت تفعل ذلك حقًا.
إذن حتى الوصفات لها البنية نفسها. هناك تسلسل من الخطوات، وهناك تدفّق تحكّم، مثل: إن لم يكن لديك بيض فاستبدل بيضًا بديلًا، أو كرّر غرس عيدان بمقدار مرة كل دقيقة للتحقّق مما إذا كانت تخرج نظيفة. ثم هناك طريقة للتوقف: حين تخرج العيدان نظيفة، تُخرج الكعكة من الفرن وتأكلها.
إذن الحاسوب آلة تنفّذ هذه الخوارزميات. والحاسوب في الحقيقة غبيّ. ليس ذكيًا حقًا. لا يتخذ قرارات من تلقاء نفسه، إنما يتبع تسلسلات الخطوات التي أمنانا إياها. الحاسوب ممتاز في تخزين كميات كبيرة جدًا من البيانات؛ لا يمكننا معذلك هذا، لكن الحاسوب يستطيع تخزين غيغابايتات بل تيرابايتات أيضًا. ويستطيع إجراء العمليات بسرعة كبيرة حقًا، وهذا شيء لا نستطيع فعله. الحاسوب جيد في هذين الأمرين، لكنه ليس ذكيًا حقًا؛ لا يستطيع اتخاذ قرارات إلا إن أُمر بذلك.
إذن في الحقيقة، الحاسوب لا يفعل إلا ما تأمره به؛ تسلسلات الخطوات التي تأمره بها، هذا كل ما يتبعه. وهذه واحدة من الأفكار الكبرى التي أريد أن تخرج بها من هذه المحاضرة.
إذن لنعطيك تاريخًا مختصرًا حقًا، لنجعل البرمجة لك تقدّر لغة Python قبل أن نبدأ فعلًا. قبل الأربعينيات من القرن الماضي، كان لدينا ما يُسمى الحواسيب ذات البرامج الثابتة (Fixed program computers). آلة حاسبة جيبية مثال على ذلك؛ كل زر كان عملية، وفي الشاشة الصغيرة كان بإمكانك استخدام الأقواس لتجميع عمليات مختلفة معًا، لكن لم يكن هناك طريقة لتخزين كل هذه العمليات معًا لتُدخل لاحقًا مدخلات مختلفة لتسلسل العمليات نفسه. كان عليك إدخالها في كل مرة.
بعد الأربعينيات، ظهرت الحواسيب ذات البرامج المخزَّنة (Stored programs computers)، وكانت قادرة على تخزين تعليمات تنفيذ الأشياء بوصفها بيانات. وكان هناك برنامج خاص اسمه المُفسِّر (Interpreter) ينفّذ هذه التعليمات. كان يعرف كيف يتبع تسلسلات بسيطة من الخطوات، وحين كان البرنامج يطلب منه الانتقال إلى موضع مختلف كان ينتقل. إذن كان في الأساس ينفّذ هذه التعليمات.
والتعليمات التي كان ينفّذها كانت حسابية ومنطقية: الجمع، والطرح، وأشياء من ذلك؛ واختبارات بسيطة مثل التحقّق من التساوي بين قيمتين؛ وتحريك البيانات، أي أخذ هذه القيمة ووضعها في موضع ذاكرة مختلف.
صفحة المصدر 4
أريد فقط أن أعطيك نظرة عامة مختصرة جدًّا، وهذه ليست دقيقة إلى حد كبير، لكنها تعطيك إحساسًا بكيفية حدوث الأمور على مستوى منخفض داخل الحاسوب. فالحاسوب لديه ذاكرة (Memory) حيث تُخزَّن الأشياء، ولديه وحدة الحساب والمنطق (Arithmetic Logic Unit) التي تُجري العمليات؛ تعرف كيف تجمع الأشياء وتطرح وتضرب وتقارن. ثم لديه وحدة التحكم (Control unit)، وحيث يُضبط عدادُ البرنامج (Program counter) هذا، وهنا تضع البرنامج.
حسنًا، لنرَ إن كان هذا سيعمل. هذا برنامج. وفوق هنا لدينا ذاكرتنا. إذن لدينا مجموعة من مواقع الذاكرة، 3456، 3457. وفي كل واحد من مواقع الذاكرة هذه لدينا بعض القيم المخزَّنة مملوءة مسبقًا. إذن حين نشغّل هذا البرنامج أول مرة، ما يحدث هو أن المُفسِّر يرى أول تعليمة، وهي Add، يجمع القيم في 3456 و 3457 معًا. إذن يذهب إلى موقعي الذاكرة هنا، ويأخذ 3 و 4، ويرسلهما إلى وحدة الحساب والمنطق.
تعرف وحدة الحساب والمنطق كيف تُجري الجمع، فتجمع 3 على 4 فيكون 7، وترسل النتيجة إلى هنا. الآن، لم نُمَرْر أبدًا لتخزين تلك النتيجة في أي مكان. لكن التعليمة التالية تقول: خزّن القيمة التي عدت للتو من وحدة الحساب والمنطق في موقع الذاكرة هذا، وهو 3458. إذن تأخذ الخطوة التالية تلك الـ7 وتخزّنها في موقع الذاكرة 3458. مملّ حقًا؛ كل ما فعلناه هو جمع 3 على 4.
نفعل ذلك من جديد. نجمع القيم في 7889 و 7890. فيذهب إلى الذاكرة، يأخذ 5 و 2، ويرسلهما إلى وحدة الحساب المنطقية. تحسبها مساويةً لـ7 وتعيدها، ثم نخزّنها في الموقع 7891. وبعد ذلك، كل ما فعلناه هو عمليتا جمع. ثم تقول التعليمة التالية: قارن القيم في موقعَي الذاكرة 3458 و 7891. إذن سنقارن 7 بـ7.
وتقوم وحدة الحساب المنطقية بهذا المقارنة مجددًا وتقول: حسنًا، 7 و 7 متكافئان. إذن هذا صحيح، أو أي شيء يريد أن يعيده إلى المُفسِّر. ثم آخر تعليمة لدينا هنا هي: اطبع نتيجة تلك المقارنة. إذن نطبع True لأنهما متساويتان. مرة أخرى، مستوى عالٍ حقًا، لكنه يعطيك نوعًا من التقدير للغات البرمجة في أيامنا؛ هذا ممل حقًا في الكتابة لو كان علينا كتابة البرامج بهذه الطريقة.
آلا تورينغ، قبل زمن طويل، أثبت أن بإمكانك حساب أي شيء باستخدام في الحقيقة مجموعةٍ أكثر أولية من الأوليات (Primitives) — لا الجمع ولا الطرح، بل، وبدلًا من ذلك، بشريط (Tape) تكون لديك ست أوليات: حرّك الشريط يسارًا، حرّك الشريط يمينًا، اقرأ القيمة عند الشريط، ضع قيمة على الشريط، امسح القيمة من الشريط، ولا عملية.
وإذن، بما أنه أثبت هذا، فالنتيجة الفعلية هي هذه: أي شيء قابل للحساب في لغة ما هو قابل للحساب في أي لغة برمجة أخرى. فإذا كان لدينا برنامج مكتوب في Java، فإن ذلك ينزل في الأساس إلى شيء طويل جدًا لكنه مصنوع من هذه الأوليات الست. هذا يعني أنه إذا اختزلنا هذا البرنامج إلى هذه الأوليات الست، يمكننا أن نبني البرنامج نفسه في لغة مختلفة تمامًا.
وهذا قوي حقًا؛ عبارة رائعة حقًا. الآن، لن نعمل بهذه الأوليات، بل سنستخدم أوليات Python التي هي أكثر ملاءمة وتتيح لنا أن نفعل الكثير في وقت أقل بكثير. سأجري مقارنةً صغيرة بينما نتحدث عن أوليات Python مقارنةً بالإنجليزية.
صفحة المصدر 5
آنا بيل: إذن في الإنجليزية، بعض الأوليات قد تكون كلمات، بل يمكننا حتى استخدام الحروف أو المحارف. لكن يمكننا القول إنها كلمات. بالمحارف يمكننا بناء الكلمات، وبالكلمات نبني الجُمل، وبالجُمل نبني القصص، وبالقصص نبني الكتب وأشياء من هذا النوع.
وفي لغات البرمجة، الأوليات هي الأعداد، وتسلسلات المحارف، والمعاملات مثل الجمع والضرب والقسمة، والتحقّق من التساوي، والتحقّق من أن شيئًا أكبر من شيء آخر، وأشياء من هذا النوع. إذن بعد أن تتوفّر لدينا هذه الأوليات في لغة ما، يمكننا أن نبدأ في بناء اصطلاح اللغة (Syntax) فوقها.
إذن في الإنجليزية، وجود «اسم + اسم + اسم» لا يعني شيئًا. «قطة كلب فتى» لا يعني الكثير؛ إنه غير صالح اصطلاحيًا. لكن «اسم + فعل + اسم» صالح اصطلاحيًا. وبالمثل، في لغات البرمجة، يمكننا أن يكون لدينا كائنان جنبًا إلى جنب. فهنا، هذا هو تسلسل محارف h و i. وهذا هو العدد 5 بجوار ذلك التسلسل. لكن ذلك لا يعني شيئًا، صحيح؟ ما معنى وجود تسلسل المحارف هذا والعدد ذلك جنبًا إلى جنب؟ لا معنى له في Python. وبدلًا من ذلك، علينا أن نضع معاملًا (Operator) بين هذين الكائنين. فنضع هنا عامل النجمة الصغير بين تسلسل المحارف "hi" والعدد 5. وفي Python، معنى ذلك: أريد أن أكرر تسلسل المحارف "hi"، أي h-i، خمس مرات. فسيعطيني هذا في الأساس hi، hi، hi، hi، hi.
إذن بعد أن تتوفّر لدينا جُمل في الإنجليزية وتعبيرات صالحة اصطلاحيًا، يمكننا الآن التحدث عن الدلالات الثابتة (Static semantics) للغة. ففي الإنجليزية، قول شيء مثل «أنا are جائع» صحيح اصطلاحيًا، لكنه ليس صحيحًا — سامحيني، ليس له دلالات ثابتة جيدة. لا معنى لذلك، لأن «are» تُستخدم مع «أنت» أو مع الجمع.
وبالمثل، في لغات البرمجة — وسيختلف هذا حسب لغة البرمجة التي تستخدمها — هنا، في الشريحة السابقة، رأينا أنك تستطيع استخدام عامل النجمة بين تسلسل المحارف والعدد. وكان ذلك يعني: كرّر هذا التسلسل مرارًا. لكن إذا استخدمنا عامل الجمع بين تسلسل المحارف والعدد، فلا معنى لذلك في Python. إذن لديه خطأ دلالات ثابت (Static semantic error)، رغم أنه صالح اصطلاحيًا، صحيح؟
لدينا عامل — سامحيني، كائن، عامل، كائن. إذن حتى الآن، تمكّنّا من إيجاد توازيات جميلة حقًا بين الإنجليزية ولغات البرمجة. لكن هذا هو نوعًا ما الموضع الذي تبدأ فيه الأمور بالتعثّر، حين نتحدث عن دلالات اللغة. ففي الإنجليزية، يمكن أن يكون للمعاني المتعددة معانٍ كثيرة. «الدجاجة جاهزة للأكل» تعني فلنأكل هذه الدجاجة. أو «الدجاجة جاهزة للأكل» تعني الدجاجة تريد أن تأكل شيئًا.
أما في لغات البرمجة فلا يوجد معانٍ متعددة لما تكتبه من برامج. لأن الحاسوب، الآلة، اللغة تتبع مجموعة التعليمات إلى حرفها؛ فلا يوجد التباس فيما يلزمه فعله. إنما يتبع التعليمات ويفعل ما يلزمه فعله حتى النهاية، حتى يبلغ نهاية البرنامج.
إذن البرامج لها معنى واحد فقط، لكن المشكلة أن هذا المعنى قد لا يكون هو المعنى الذي قصدته. وعندئذ تبدأ الأمور بالسوء. يمكننا أن يكون لدينا أخطاء اصطلاحية (Syntactic errors) في برنامجنا، أخطاء إملائية، وأخطاء في المسافة البادئة (Indentation)، وأشياء من هذا النوع. وهذه سهلة الالتقاط. أخطاء الدلالات الثابتة يُحتمل أن تكون 90% سهلة الالتقاط. لكن المشكلة تأتي مع الدلالات؛ المعنى الذي قصدتَ لهذا البرنامج أن يكون قد لا يكون ما يفعله فعلًا.
صفحة المصدر 6
آنا بيل: وهناك تقع معظم أخطائي. وهناك أشعر بالضيق الشديد عندما أبرمج. وهناك أيضًا من المرجح أن تشعرون بالطع بالضيق الشديد، لأنكم تكتبون برنامجًا تظن أنه يفعل شيئًا واحدًا، فينتهي إما إلى أنه ينهار فورًا، أو يعمل إلى الأبد ولا يتوقف حقًا، أو ينتهي لكنه يعطيك إجابة غير صحيحة. ليس هذا ما كنت تتوقعه. وسنتحدث عن هذا في محاضرات قليلة قادمة.
إذن حين نكتب برامج، فنحن في الأساس نكتب تسلسلات من التعريفات والأوامر. وسنكتبها إما في محرّر ملفات (File editor) أو في الصدفة (Shell). الأولى — اليوم على الأقل — سنكتبها مباشرة في الصدفة. وفي نصف محاضرة الغد سنكتب في الصدفة أيضًا، لأننا لن نكتب حقًا أي أسطر شيفرة كثيرة؛ كل ما سأفعله هو أن أعرض عليكم بعض الأشياء السريعة جدًا التي يمكننا فعلها بلغة Python.
آمل أن يكون لديكم جميعًا بيئة البرمجة مثبَّتة. هذا هو محرّر الشيفرة. غدًا سنبدأ العمل هنا. لكن اليوم سنعمل في الصدفة حقًا. وحتى في المستقبل، ما زال بإمكانك كتابة الأوامر في الصدفة. وأجد الصدفة مفيدة جدًا إذا كان هناك شيء سريع جدًا أريد التحقّق منه، ولا أريد أن أكتب له برنامجًا ثم أشغّله. إنه ببساطة أمر بسيط أريد التحقّق منه أنه يفعل ما أظن أنه يفعله، قبل أن أُدرجه في محرّر الشيفرة.
إذن هذا ما لدي. سماحي — أظن أنني أستخدم النمط الأبيض لمجرد أن أجده أسهل لكم في الرؤية. هذا هو محرّر الملفات. وهذه مجموعة من التعبيرات أو — نعم، مجموعة من الشيفرة التي سنكتبها اليوم. وسنكتبها في الصدفة اليوم، أي الشيء على الجانب الأيمن.
حسنًا، إذن ماذا نفعل بالضبط حين نكتب برنامجًا؟ في الأساس، سننشئ كائنات (Objects) داخل برامجنا، ثم نُعالجها. هذا كل شيء. هذا هو جوهر البرمجة إلى حد كبير. الآن، حين ننشئ كائنات، من المهم — وهذا شيء سنعود إليه مرارًا في سياق أكثر ارتفاعًا — أن نُدرك أنه حين ننشئ كائنًا، للكائن نوع (Type). والنوع الذي للكائن يحدّد الأشياء التي يُسمح لكم بفعلها بذلك الكائن.
إذن إليك مثالين. العدد 30، إنه عدد. سنتحدث عن النوع بعد قليل. النوع هو عدد صحيح (Integer)، أي عدد كامل. لكن ما الأشياء التي يمكننا فعلها بهذا العدد الصحيح؟ يمكننا جمعه إلى عدد آخر. ويمكننا طرحه من عدد آخر. ويمكننا رفعه إلى أُس آخر. ويمكننا رفع عدد آخر إلى أُس هذا العدد ثلاثين. مجموعة من العمليات الرياضية، كما تتوقع. إذن هذا الأمر بسيط إلى حد ما.
وماذا عن هذا هنا، علامات الاقتباس ثم A كبيرة، وحرف a صغير — n، ثم a صغير، ثم علامة اقتباس؟
إذن هذا هو شيء سنتحدث عنه في المحاضرة القادمة. يُسمّى سلسلة نصية (String). وهو تسلسل من المحارف. وعلامات الاقتباس تخبر Python أنه تسلسل محارف. والمحارف التي يحتويها هي A كبيرة، وn صغير، وa صغير. والأشياء التي أستطيع فعلها بهذه السلسلة النصية ليست نفسها الأشياء التي يُسمح لي بفعلها بالعدد، صحيح؟
فإن حاولت آخذ Ana وأقسمها على تسلسل المحارف Bob، لشكا Python كثيرًا، لأنك لا تستطيع قسم سلسلة نصية على سلسلة نصية أخرى، أو تسلسل محارف. لا معنى لقسمتها على تسلسل محارف آخر.
صفحة المصدر 7
آنا بيل: وبالمثل، لا أستطيع رفع Ana إلى أُس ما. لا أستطيع ضربها — لا أستطيع ضربها في نفسها، وأشياء من هذا النوع. لكن الأشياء التي يُسمح لي بفعلها على تسلسل محارف تختلف عن الأشياء التي يُسمح لي بفعلها على عدد. فالأشياء التي أستطيع فعلها بتسلسل محارف هي: ما المحرف في الموضع الأول؟ ما المحرف الأوسط؟ ما طول تسلسل المحارف؟ كم محرفًا لديّ؟
إذن ترون الآن أن نوع الكائن مهم حقًا. يستخدمه Python ليعرف أنواع العمليات المسموح بها معه. وهناك في الواقع كائنات مُقيسة (Scalar objects)، وهذه هي أوليات Python: الأعداد وقيم الحقيقة. وهناك كائنات غير مُقيسة (Nonscalar objects). لسنا نتحدث عنها الآن؛ سنتحدث عنها بعد محاضرات قليلة. لكن لها بعض البنية. فمثلًا، قائمة من الأعداد لها بنية، لأن هناك عددًا في بداية القائمة، وعددًا في نهاية القائمة، وأشياء من هذا النوع.
لكن العدد نفسه ليس له بنية. إنه مجرد العدد. إذن ما أنواع الكائنات المُقيسة؟ ما أنواع الأوليات في Python؟ أعداد صحيحة (Integers)، مثل العدد 5، و0، و100 السالب، والمليون.
الأعداد العشرية (Float) نوع آخر. وهي تمثّل كل الأعداد الحقيقية، مثل 3.27. و2.0 عدد عشري لأنه يحتوي على خانة عشرية، رغم أن معناها بالنسبة لنا هو اثنين فقط. لكن بالنسبة إلى Python، إذا أدخلت 2.0، فإنه يقول إن نوعه float. والعدد السالب 3.14159، وأشياء من هذا النوع.
القيمة المنطقية (Bool) هي قيمة بوليانية. وهي تمثّل قيم الحقيقة. ولا يوجد إلا قيمتان محتملتان للنوع البولياني: True و False. ويجب أن تكون T كبيرة في True و F كبيرة في False.
وآخر واحد هو هذا النوع NoneType. اسمه حرفيًا NoneType. وله قيمة خاصة واحدة فقط وهي None. لن نتحدث عنه قليلًا، لكننا سنتحدث عنه في المستقبل. إذن لمعرفة نوع كائن حين تنشئه، تستخدم الأمر type.
فيمكننا أن نقول شيئًا مثل type بين قوسين. وهذا أمر. وداخل القوسين، تقول: ما الذي تريد معرفة نوعه؟ فإذا فعلنا type للعدد 7، فإنه يخبرني إنه int. وإذا أردت تنفيذ الأمر نفسه مرة أخرى،ضغطت السهم لأعلى، فيضع تلقائيًا ما كتبته سابقًا. ثم إن أردت type للعدد 0.0، فهو float، لأن هناك خانة عشرية.
إذن هذا في الأساس هو ما قلته. فنكتب هذا في الصدفة. والصدفة تخبرنا بما هو الخرج. ولأكرر: int و float و bool و NoneType هي أنواع كائنات. ويمكن أن تُنشئ كائنات مختلفة من ذلك النوع.
فإن فكّرت في الأمر، فأعداد int و float لدينا في الأساس عدد لا نهائي من الكائنات التي يمكننا إنشاؤها من هذين النوعين، لاننا 5، 0، 100 السالب، والمليون، وأعداد أخرى من هذا النوع. يكاد يكون عدد قيم الكائنات التي يمكننا إنشاؤها من نوع int و float لا نهائيًا.
لكن bool، فيه قيمتان فقط، قيمتا الحقيقة True أو False. أما NoneType، فيه قيمة واحدة فقط، وهي None. إذن هذا هو النوع، وهذه هي القيم الممكنة، أي الكائنات الممكنة التي يمكننا إنشاؤها. جرّب بنفسك. فيمكنكم فقط أن تقولوا الإجابات بصوت عالٍ. لا شيء لتكتبوه إلا إذا أردتم التحقّق من نفسك. ما نوع 1234؟
الحضور: int.
صفحة المصدر 8
آنا بيل: int. وما نوع 8.99؟ float. وما نوع 9.0؟ float. وما نوع True؟ bool. وما نوع False؟ bool. ممتاز. إن شعرتم يومًا ما بالتساؤل عن نوع شيء ما، فاكتبوه هنا. صعب، اكتبوا نوعه هنا، وهو bool. أما كتابة true بحرف t صغير فهي خطأ، وأردت فقط أن ألفت انتباهكم إلى أن حالة الأحرف مهمة في Python.
هذا أول خطأ لدينا بالمناسبة. مثير جدًّا. الخطأ هو NameError، وهذه هي الرسالة المرتبطة به. ويمكنكم أيضًا أن تعرفوا أن الأمر في Python مميز عندما تكون هناك عناصر ملوّنة بالألوان. فترون أن True ذات T كبيرة و False ذات F كبيرة تظهران هنا باللون الأزرق الداكن، في حين أن كل ما ليس مميزًا في Python يظهر باللون الأسود. إذن type أمر خاص. وهذا عدد عشري، فترون أنه ملوّن بالألوان.
حسنًا. إذن بعد أن ننشئ كائنات، أحد الأشياء التي يمكننا فعلها بهذه الكائنات هي تحويلها (Cast) إلى نوع مختلف. الآن، هذا الأمر ربما يكون مربكًا قليلًا، لأننا لا نغيّر الكائن فعليًا بعد أن نُنشئه. فبعد أن ننشئ العدد الصحيح 3، فإنه موجود في الذاكرة. وإذا حوّلنا ذلك العدد الصحيح إلى نسخة عشرية منه، فنحن نُنشئ كائنًا جديدًا في الذاكرة.
نحن لا نغيّر الـ3. فالـ3 موجود بالفعل. نحن فقط نأخذ النسخة العشرية منه ونخزّنها ككائن جديد في الذاكرة. فحين نفعل float للعدد 3، فهذا أمر يعطيني النسخة العشرية من العدد الصحيح 3. سيعطيني ذلك 3.0. فمثلًا، هذا ما لديّ: float للعدد 3، والخرج هو 3.0.
وإن فعلت int للعدد 5.2، فإنه يقتطعه ويعطيني الجزء الصحيح من هذا العدد العشري. وإن فعلت int للعدد 5.9، فإنه يقطعه أيضًا ويعطيني النسخة الصحيحة من هذا العدد العشري. لا تقريب. أنا فقط أطلب النسخة الصحيحة من هذا العدد العشري.
وبعض العمليات مثل round هي عملية تحويل ضمني ضمني. فإذا قرّبنا 5.9، فستتقريبها فعليًا إلى 6.0 ثم تحوّلها إلى عدد صحيح. لاحظ إذن أنه لا يعطيني 6.0 كخرج. ثم يقرّبها إلى ستة ببساطة. إذن هذا في الأساس هو ما قلته في المثال.
إذن جرّب بنفسك. ما الأنواع التالية؟ لا أحتاج القيم بل الأنواع. فإذا أخذت type لـ float من 123، فما نوع ذلك؟ float، نعم، صحيح تمامًا. وماذا لو قرّبت 7.9؟ فما نوع النتيجة؟ int، نعم. وماذا لو أنشأت عددًا عشريًا من تقريب 7.2؟
الحضور: float.
آنا بيل: نعم، جيد. float يعني 7.0. وماذا عن int للعدد 7.2؟
الحضور: int.
آنا بيل: int، نعم، صحيح تمامًا. أريد النوع لا القيمة. وint للعدد 7.9 هو int، صحيح تمامًا. رائع، جيد.
حسنًا، إذن لقد أنشأنا مجموعة من الكائنات. ونعرف أننا يمكننا إنشاء مجموعة من الكائنات في برامجنا. ماذا نفعل بها؟ حسنًا، يمكننا دمجها في تعبيرات (Expressions). فلنقل لدينا 3 زائد 2. لدي كائن، عامل، كائن. رائع، صالح اصطلاحيًا في Python ولا خطأ دلالات ثابت.
فإذا فعلت ذلك في Python فسيكون الأمر حسنًا. 3 زائد 2 يساوي 5. ونوع 3 زائد 2 هو عدد صحيح. إذن في الأساس ما فعلته هنا هو أنني وضعت تعبيرًا داخل أمر type. وهذا حسن. بل هو مشجَّع في Python. لا تريد أن تحسب ثم تضع الناتج فقط؛ سيكون ذلك مرهقًا جدًا. لذا يمكنك إدراج التعبيرات في أماكن كثيرة جدًّا.
صفحة المصدر 9
آنا بيل: إذن هنا لدينا 3 زائد 2، و5 على 3. مرة أخرى، لدينا 5 على 3، ولها هذه القيمة العشرية. والناتج من نوع float. إذن أهم شيء نتذكّره حين نُنشئ تعبيرات (Expressions) هو أن Python يقرأ التعبير، لكنه لا يخزّن التعبير في الذاكرة. ما يفعله هو أنه يقرأ التعبير، ويقيّمه في قيمة واحدة، ثم يخزّن قيمة الناتج في الذاكرة. فهو لا يخزّن التعبير أبدًا. إنه يقيّم التعبير ثم يخزّن القيمة.
آنا بيل: وهذه هي صياغة (Syntax) التعبير: كائن، عامل، كائن، كما رأينا للتوّ. وهذه في الحقيقة — والفكرة التي قلتها من قبل، حيث إن Python تخزّن قيم التعبيرات لا التعبيرات نفسها، مهمة جدًّا، جدًّا. إذن هذه هي شريحة «الفكرة الكبرى» الأولى التي اخترت أن أدرجها، لأنني أعتقد أنها توكّد أهمية عدة مفاهيم. آمل أن تكون إحداها. إذن نحن نأخذ التعبيرات. يمكن أن تكون معقّدة بقدر ما تريد. يمكننا أن نستخدم الأقواس، وكثيرًا من — لا يجب أن يكون الأمر مجرّد كائن، عامل، كائن. يمكن أن يكون أعقد من ذلك.
آنا بيل: لكن في الأساس، مهما كان ذلك التعبير معقّدًا، فإننا نقيّمه، ثم نستبدله بقيمة واحدة. ويمكن أن يكون التعبير شيئًا من هذا النوع. لا يجب أن يكون شيئًا رياضيًا. كان هذا تعبيرًا رياضيًا، لكن هذا أيضًا تعبير. وهو يُقيَّم. إذن هذا كله يُقيَّم إلى هذه الكلمة، وهي هذه الكلمة التي تمثّل النوع int.
آنا بيل: وهنا بعض الأمثلة الإضافية. 3 زائد 2، مرة أخرى. لدينا هذه الأمثلة مع الأقواس، و4 زائد 2 مضروب في 6 ناقص 1، من البديهي أنه يعطينا العدد 35. ثم يمكننا أن ندخل التعبيرات أينما نرغب. فنحن هنا ندخل ذلك التعبير تحديدًا في الأمر type. وهذا أيضًا تعبير، كما قلت للتوّ. ونتيجته هي int. وبالمثل، يمكننا أيضًا إدخال ذلك التعبير هنا. ثم يمكننا أن نلفّ ذلك بـfloat. وهو يعطينا float. صحيح؟
الحضور: إذن حين تدخلون تعبيرات… هل تشمل العوامل — هل تشمل العوامل؟
آنا بيل: حين تدخلون — آسفة، حين تدخلون ماذا؟
الحضور: حسنًا، بما أنكِ قلتِ إنها تعبيرات، وقلتِ إننا نحتاج مثلًا إلى كائن، عامل، كائن، تعبير، type. فما هي العوامل في هذه الحالة؟
آنا بيل: أوه، فهمت.
الحضور: كيف تُعرَّف؟
آنا بيل: نعم، هذا سؤال جيّد — سؤال جيّد فعلًا. إذن في هذه الحالة تحديدًا، فإن type وfloat ليسا — لا يوجد عامل في هذه الحالة تحديدًا، على حدّ علمي. أقرب إلى أنها أمرٌ يعطينا خرجًا. لكن ما زال هناك شيء — ما زال هناك خرج يعطينه إياه. يمكننا بعد ذلك أن نأخذ نتيجة هذا ونحفظها في مكان آخر. آسفة، نعم، أظن أن المثال الذي أعطيته في الشريحة السابقة كان مجرّد مثال على تعبير يمكننا أن نفعل فيه كائن، عامل، كائن. نعم.
آنا بيل: نعم، إذن حين يكون لدينا هذه — أظن أن الأمر ينطبق على التعبيرات الرياضية. التعبيرات الرياضية تُنفَّذ من اليسار إلى اليمين، تمامًا كما في الرياضيات. والأقواس يمكنها أن تتجاوز بعض أولويات التقييم. فإذا كان لدينا أوامر تحتوي على حسابات، لدينا هذا الأمر مع الأقواس. ونقيّم ما بداخل الأقواس أولًا. فنعمل في تلك الحالة من الداخل إلى الخارج.
صفحة المصدر 10
آنا بيل: إذن هنا بعض الأمثلة. لنجعلكم تجرّبون هذه. يمكننا أن نكتبها في وحدة التحكّم (Console). ما قيم التعبيرات التالية؟ إذن 13 ناقص 4 مقسومًا على 12 مضروبًا في 12. يمكننا أن نجرّب ذلك. لا أعرف من رأسي الآن، لذا سنحتاج إلى كتابتها. 0.0625، حسنًا. إذن قيمة ذلك التعبير هي float، صحيح؟ 0.0625. ما قيمة التعبير type عدد 4 مضروبًا في 3؟
ملاحظة المترجم: نُقلت العبارة المنطوقة بلا أقواس، لكن شريحة المحاضرة تكتب التعبير
(13-4) / (12*12)، وقيمته 0.0625 كما ورد في النص الرسمي.
الحضور: int.
آنا بيل: int، نعم. وماذا عن نوع التعبير 4.0 مضروبًا في 3؟
الحضور: float.
آنا بيل: نعم، بالضبط. هذا جيّد جدًّا. إذن نوع 4 مضروبًا في 3 هو int. لكن 4.0 مضروبًا في 3 هو float. جيّد. ثم ماذا عن int للنصف، أو int لثلث؟
الحضور: إذن هو 0.
آنا بيل: نعم، بالضبط، هو 0. صحيح، لأنه 0.5، ونقتطعه إلى 0. السبب في أنني وضعت هذا هنا لأنه يؤدّي بشكل جيّد إلى الشريحة التالية. لستم مضطرين إلى حفظ هذه القواعد. يمكنك دائمًا التحقّق منها في وحدة التحكّم. لكن هناك بعض القواعد لأنواع الناتج حين نُجري عمليات. فحين نُجري عمليات على أعداد، فإن الجمع والطرح والضرب تُنتج دائمًا عددًا صحيحًا إذا كان كلا المعاملين عددين صحيحين. فاذا كان أحدهما float أو كلاهما float، فإنه يعطيني float. أما القسمة فهي مختلفة. مهما كانت أنواع ما تقسمه، فسنحصل دائمًا على float. وماذا عن هذا // وهذه علامة النسبة المئوية؟ هذان في الواقع عمليات مفيدة. وهما تمضيان معًا نوعًا ما مع القسمة. فحين أقسم 5 على 3، نحصل على 1.667.
آنا بيل: و// في جوهرها هي الأرضية، أو أخذ الجزء الصحيح من القسمة. فـ5//3 يعطيني 1. فهي تقتطع الكسر. وعلامة النسبة المئوية تعطيني الباقي. فـ5%3 تعطيني الباقي حين أقسم 5 على 3. إذن ستعطيني — تعطيني إياه في عدد صحيح. لذا سيكون 2، لأن هناك 2 يتبقّيان حين أقسم 5 على 3. فهذه عمليات مفيدة إلى حدٍّ كبير، // وعلامة النسبة المئوية، حين نكتب برامج رياضية. وآخر شيء هو أن ** هي كيف نرمز إلى الأس، أي الرفع إلى قوة، وهو مختلف نوعًا ما عمّا قد تكون معتادًا عليه في الرياضيات. فـ2 مرفوعة إلى القوة 3 تساوي 8. و2 مرفوعة إلى القوة 3.0 تساوي 8.0. وقواعد القسمة الصحيحة وعلامة النسبة المئوية والرفع إلى قوة هي تمامًا مثل الجمع والطرح والضرب. إذا كان أحدها float، فإن الناتج سيكون float أيضًا.
آنا بيل: نعم. حسنًا، وتحدثنا عن نوع الخرج. إذن أظن أنني ذكرته بإيجاز. أولوية المعاملات (Operator Precedence) هي: الرفع إلى القوة أولًا، ثم الضرب والقسمة وعلامة النسبة أو الباقي في المستوى التالي، ثم الجمع والطرح في الأسفل. لكن يمكنك دائمًا تجاوز هذه الأولويات باستخدام الأقواس.
آنا بيل: حسنًا، هل من أسئلة حتى الآن قبل أن ننتقل؟ نعم.
الحضور: إذن لماذا القسمة — لماذا تنتج دائمًا float إذا كان لديك 9 على 3، وهذا… لماذا ينتج [غير مسموع]؟
آنا بيل: نعم، فالسؤال هو: لماذا ينتج دائمًا float؟ لو لم يفعل، فأظن أن العملية نفسها كانت ستفعل عملًا إضافيًا لتعرف ما إذا كان عددًا صحيحًا أم لا. لذا أظن أن الأمر أسهل أن يعطينا float دائمًا. في الإصدارات السابقة من Python، كان / في الواقع — أظن — قسمة أعداد صحيحة، وهو أمر غريب إلى حدٍّ كبير، لأنك ستستخدم ذلك في برنامجك. ثم ستقوم فعليًا بقسمة أعداد صحيحة، وستسوء الأمور. لكن مرة أخرى، هذه مجرد اختيار تصميم من المبرمجين. أي أسئلة أخرى حتى الآن؟
صفحة المصدر 11
آنا بيل: حسنًا، إذن لدينا عدد كبير من الكائنات. والكائنات لها أنواع مختلفة، مرة أخرى: أعداد عشرية، وأعداد صحيحة، وقيم بوليانية. وماذا يمكننا أن نفعل بها؟ حتى الآن هي في جوهرها جالسة هناك، يمكننا أن نحصل على خصائص عنها. لكن ما نودّ فعله هو كتابة برامج، أي محاولة أتمتة بعض الأشياء حول هذه الكائنات، ومعالجتها لت مساعدتنا على تحقيق برنامج أكثر تعقيدًا وإثارة.
آنا بيل: إذن ما يمكننا فعله للوصول إلى ذلك هو أن نبدأ بإعطاء أسماء لبعض هذه الكائنات. فإذا أنشأت كائنًا لـ pi في برنامجي إلى عشرين خانة عشرية ما، وكان لديّ ذلك العدد في برنامجي، ذلك الـ float في برنامجي — فإذا أردت استخدام ذلك العدد في أماكن مختلفة كثيرة في برنامجي، لسوء الحظ سأضطر إلى نسخه ولصقه مرات ومرات، وهذا مرهق إلى حدٍّ كبير، وستحدث أخطاء كثيرة. لا أريد أن أفعل ذلك.
آنا بيل: لذا بدلًا من ذلك، ما أستطيع فعله هو أن أعطي اسمًا تلك القيمة الطويلة لـ pi. ثم أستطيع أن أستخدم هذا الاسم في أي مكان أريد فيه استرجاع تلك القيمة الطويلة لـ pi في برنامجي. القراءة أسهل بكثير. وكتابة هذا البرنامج أسهل بالنسبة لي كثيرًا. ويؤدّي إلى برنامج جميل ومرتّب حقًا.
آنا بيل: إذن ما يمكننا فعله هو أن نبدأ في القول إن الـ float بقيمة 0.001 سيُشار إليه بالاسم small، أو القيمة 100.4 ستُشار إليها بالاسم temp. إذن ما نريد فعله هو إنشاء هذه الأشياء التي نسمّيها متغيّرات (Variables). والمتغيّر في علوم الحاسوب يختلف عن المتغيّر الرياضي أو المتغيّرات التي عرفتها حتى الآن في الرياضيات.
آنا بيل: فمتغيّرات الرياضيات تعود إلى فكرة المعرفة التقريرية (Declarative Knowledge)، أي عبارة تقريرية. يمكنك في الرياضيات أن تكون مثل a + b يساوي b ناقص 1، أو x يساوي — أو x مضروبًا في x يساوي y، وهذا تمام. في الرياضيات، نحن نقول أساسًا إن المتغيّر x يمثّل كل الجذور التربيعية لـ y. لن ينجح هذا في علوم الحاسوب.
آنا بيل: في علوم الحاسوب، ليس لدينا — نحن لا نستخدم المعرفة التقريرية. نحن نستخدم المعرفة الأمرية (Imperative Knowledge). إذن ما نتعامل معه في علوم الحاسوب هو مجموعة من عبارات الإسناد (Assignment). إذن ما يمكننا فعله في علوم الحاسوب هو أننا سنربط قيمةً بمتغيّر. سنقول إن اسم هذا المتغيّر مرتبط بهذه القيمة. في كل مرة أريد فيها استرجاع هذه القيمة، سأستدعي اسم هذا المتغيّر. وإليك بعض الأمثلة.
آنا بيل: لديّ a يساوي b زائد 1. إن كان لديّ شيء له قيمة عند b، فإن الشيء على الجانب الأيمن سيُقيَّم إلى قيمة ما. ولديّ هنا m يساوي 10. إذن m متغيّر. وقيمته 10. ولديّ F يساوي m مضروبًا في 9.98. إذن مرة أخرى، لديّ تعبير على الجانب الأيمن، وهذا مقبول. سأستخدم القيمة 10، إذن ستكون قيمة F هي 99.8. نعم.
الحضور: هل يمكنك أن تُدير الأمر بحيث يكون F — هل هو مثل هذه القيمة الواحدة لـ m؟ أم يمكنك أن تجعله يأخذ قيمة m التي أُسندت مؤخرًا؟
آنا بيل: نعم. فالسؤال هو: هل يمكنك أن تجعل m تأخذ قيمتها الأخيرة؟ في هذه الحالة تحديدًا، لديّ هذان السطران فقط. وستكون m هي 10. لكننا سنرى بعد محاضرات أنه يمكننا كتابة حلقة تغيّر بها m. وفي كل مرة تغيّر m، تحسب F فورًا. ثم تحسب F بناءً على القيمة الجديدة لـ m. لكن إن كان لدينا هذان السطران فقط، فهذه هي كل شيء. سيستخدم 10 فقط. هل كان هناك سؤال آخر؟
آنا بيل: إذن في علوم الحاسوب، لديك متغيّر واحد فقط على يسار علامة المساواة هذه، وهي معامل الإسناد (Assignment Operator). ولديك قيمة على يمين علامة المساواة، وهي معامل الإسناد. إذن متغيّر واحد في الأساس يقابل قيمة واحدة أو يرتبط بها.
صفحة المصدر 12
آنا بيل: إذن علامة المساواة هي عبارة إسناد. إنها ليست مساواة. وليست من نوع حل المعادلة لإيجاد x. إنها مجرّد إسناد. إنها تربط هذا الاسم بهذه القيمة. إذن الطريقة التي نحدّد بها الاسم مع القيمة هي: حسنًا، إن كانت لدينا عبارة الإسناد هذه، فإننا ننظر أولًا إلى الجانب الأيمن. فدائمًا ما نبدأ بالجانب الأيمن. ونُقيّمه.
آنا بيل: تذكّر، لدينا تعبير على اليمين. علينا أن نُقيّمه إلى قيمة واحدة. إذن ستكون 3.14، أيًّا كانت، 3.14159. ثم نأخذ تلك القيمة ونربطها بالاسم pi. إذن في أي وقت أكتب فيها p-i أي «pi» في برنامجي من الآن فصاعدًا، فإن Python ستلتقط تلقائيًا 3.14159 من الذاكرة. إذن هو مرتبط بتلك القيمة الآن.
آنا بيل: حسنًا، هناك بعض القواعد. هل كانت عليّ في الشريحة السابقة؟ نعم، هناك بعض القواعد لأسماء المتغيّرات، لكننا سنتحدث عن ذلك بعد قليل. في الوقت الحالي، أريدك أن تخبرني إن كان أي ممّا يلي مسموحًا. إن كتبت x يساوي 6، فهل ذلك مسموح في Python؟
الحضور: نعم.
آنا بيل: نعم، مسموح. جيّد. لأن لديّ اسم متغيّر واحد مرتبط بقيمة واحدة، وهي 6. وماذا عن 6 يساوي x؟ إنه معكوس فقط.
الحضور: لا.
آنا بيل: حسنًا، جيّد. فـ6 يساوي x خطأ صياغة. وماذا عن x مضروبًا في y يساوي 3 زائد 4؟
الحضور: لا.
آنا بيل: لا، بالضبط، لأن الشيء في الجهة اليسرى فيه عامل. والعوامل خاصة. فلا يمكن — فلا يمكن أن تحمل قيمة ذات عامل اسمًا لمتغيّر. وماذا عن xy يساوي 3 زائد 4؟
الحضور: نعم.
آنا بيل: مسموح، نعم، بالضبط. كنت آمل أن أوقعكم في ذلك، لكنني لم أفعل. فـxy يساوي 3 زائد 4 مقبول. لم يكن هناك أي خطأ. ثم أستطيع أن أستدعي اسم المتغيّر الذي أنشأته للتو بمجرد كتابته. فإذا كتبت xy، فإنه يعطيني 7. ثم أستطيع إجراء عمليات معه، xy زائد 1 يساوي 8. نعم.
الحضور: قبل ذلك كنت تضع النصوص المحاطة بعلامة اقتباس مفردة. إذن ألا تحتاج إلى ذلك؟
آنا بيل: إذن تلك نصوص، صحيح، تسلسلات محارف. أما هنا فهذه متغيّرات. وهذه أسماء أنا أعطيها كمتغيّر. نعم، هذا سؤال جيّد جدًّا. إذن هذا سيكون نصًّا (String). ولاحظ أنه تغيّر لونه. له معنى ما في Python. لكن xy متغيّر أنا أنشئه.
آنا بيل: حسنًا، إذن لماذا نريد أن نعطي أسماء للمتغيّرات؟ لأنني، كما عرضت عليكم في مثال pi، من الأسهل بكثير كتابة شيفرة قابلة للقراءة إن كانت لديك أسماء متغيّرات داخل برامجك. إذن حين تسترجع — حين تكتب برامج، من المهم أن تختار أسماء المتغيّرات بحكمة. لا تريد أن تستخدم حروفًا مفردة فقط. ولا تريد أن تسمّيه شيئًا لا علاقة له بالبرنامج الذي تكتبه، لأنك ستحتاج إلى إعادة قراءة هذه البرامج في وقت ما في المستقبل. أو قد يريد الآخرون قراءة برامجك في وقت ما في المستقبل.
صفحة المصدر 13
آنا بيل: إذن إليكم مثال على برنامج جيّد. هو في جوهره أربعة عبارات إسناد تُجري بعض الحسابات.
السطر الأول من البرنامج ليس سطرًا في الحقيقة، بل هو ما يُسمّى تعليقًا (Comment). يمكن أن يكون لديك من هذه التعليقات ما تشاء. وتبدأ بعلامة #. إنها سطر يبدأ بعلامة #، وهي في الأساس نص تكتبه يساعدك أو يساعد الآخرين على معرفة ما يفترض أن تفعله الشيفرة.
وفي العادة نعلّق على أجزاء كبيرة من الشيفرة دفعة واحدة، لا سطرًا سطرًا. ثم لدينا عبارات الإسناد الأربع هذه. إذن هنا أنا أُعرّف متغيّرًا اسمه pi مربوطًا بالقيمة الموجودة هنا، أي ليس القسمة، بل العدد 3.14159. ومتغيّر اسمه radius مربوط بهذا العدد العشري 2.2. ثم لدي متغيّر اسمه area مربوط بنتيجة هذا التعبير.
إذن حين يرى Python المتغيّرين pi وradius الخاصين بي، فإنه يلتقطهما من الذاكرة، يستبدلهما بالقيمتين، يقيّم التعبير، يلتقط تلك القيمة الواحدة التي قِيّمناها إلى 15 فاصلة وشيء، أيًّا كان ذلك، ويربط قيمة 15 فاصلة وشيء بالاسم area. والأمر نفسه مع circumference.
وسننظر فعلًا إلى نمط الشيفرة (Code Style) في مجموعات المسائل، لذلك أريد فقط أن أتحدّث عنه بسرعة. إليكم برنامج ذا نمط سيّئ جدًا حقًا. في الواقع، لا ينبغي أن يكون هذا سيّئًا فحسب، بل يجب أن يكون فظيعًا أو شيئًا من هذا القبيل. لكن إن لم تلاحظوا، فهو البرنامج نفسه في الشريحة السابقة. لكن لو أعطيتكم هذا البرنامج هكذا مباشرة، فعلى الأرجح لن تعرفوا ما الذي يفعله. إنه يعيد استخدام 355 على 113 مرتين هنا. وهو يستخدم a وc فقط كأسماء متغيّرات. ووصفه هو «نفّذ حسابات». إذن سيّئ جدًا.
وهذا أفضل قليلًا. لقد لاحظت أن 355 على 113 مستخدَم مرتين، فحفظته في متغيّر. لكن متغيّراتي ما زالت أحرفًا مفردة. وتعليقاتي سيّئة أيضًا. فأنا في الأساس أقول ما الذي تفعله الشيفرة. من فضلكم لا تفعلوا ذلك. يمكننا أن نرى أن a يساوي p ضرب r ضرب r، أي أنني أرى أنني أضرب p في r تربيعًا. ولا أحتاج إلى قراءة ذلك بالإنجليزية.
ما أودّ أن أراه هو تعليقًا كهذا. هنا أعلّق على جزء من الشيفرة. ومن لا يريد قراءة هذا الجزء يقرأ التعليق فقط، وأنا أصلًا أعرف أنني أحسب المساحة والمحيط باستخدام تقريب لـ pi. هذا تعليق جيّد جدًا، ومعه أسماء وصفية جميلة وكل ذلك.
إذن يمكننا فعلًا — بعد أن ننشئ كائنًا، متغيّرًا — آسف، بعد أن ننشئ كائنًا ونربطه بمتغيّر، أن نغيّر الروابط. فبإمكاننا أن نأخذ اسم المتغيّر ونربطه بقيمة مختلفة تمامًا. وقد لا يكون هذا مفيدًا الآن، لكنه سيصبح مفيدًا حين نقدّم تدفّق التحكم (Control Flow) في برامجنا.
إذن إعادة ربط متغيّر تعني أننا سنأخذ الاسم، سنفقد ارتباطه بالقيمة السابقة، وسنعيد ربطه بقيمة جديدة. وسأريكم كيف يبدو هذا في الذاكرة. سأستخدم هذا الشكل السحابي لأمثّل ما يحدث خلف الكواليس كلما كتبنا برامج. وهو أشبه برسوم متحرّكة صغيرة تساعدك على فهم، سطرًا سطرًا، ما يحدث بالضبط.
إذن هنا لدينا pi يساوي 3.14. فالـ3.14 الخضراء هي قيمتي في الذاكرة. السحابة هي الذاكرة. هذه هي قيمتي في الذاكرة، وهي مربوطة بهذا الاسم pi؛ وهذا هو اسم المتغيّر. وفي السطر التالي، radius يساوي 2.2، الأمر نفسه. لدي 2.2 كقيمة في الذاكرة، ككائن عندي. وradius هو اسم ذلك الكائن. أما area فيساوي p ضرب radius تربيع. إذن ما يحدث خلف الكواليس هو أنه يحسب هذه القيمة. إنه لا يخزّن التعبير. إنه يخزّن القيمة الناتجة عن الحساب، ثم يحفظها — أو يربطها — بالاسم area.
صفحة المصدر 14
آنا بيل: حسنًا، كل شيء سليم حتى الآن؟ رأينا هذه الشيفرة من قبل. رائع. إذن ماذا يحدث حين نفعل هذا، radius يساوي radius زائد 1؟ في الرياضيات كان هذا سيعني أن 0 يساوي 1. لكننا لسنا في الرياضيات هنا. نحن في علوم الحاسوب، وهذا صحيح تمامًا. نحن نتبع قاعدة الإسناد التي تقول: انظر إلى الجهة اليمنى أولًا، قيّمها، ثم اربطها بالجهة اليسرى.
إذن إن نظرنا إلى الجهة اليمنى أولًا، نرى radius. فما قيمتها؟ 2.2. نرى أن نضيف إليها 1، فيصبح 3.2. نحفظ ذلك في الذاكرة. ثم نرى علامة الإسناد. الآن نحفظها بالاسم radius. حسنًا، يمكننا أن يكون لدينا متغيّر واحد فقط مربوطًا بقيمة واحدة في كل مرة. هذه ليست رياضيات. هذه علوم حاسوب. فبإمكان radius أن يشير إلى شيء واحد فقط في كل مرة.
مع سطر الشيفرة هذا، radius يساوي radius زائد 1، فقدنا الارتباط بالقيمة 2.2، أي بهذا الكائن في الذاكرة، وأعدنا ربطه بالقيمة 3.2. وهذا صحيح تمامًا. أما 2.2 فهي الآن مجرّد قيمة راكدة في الذاكرة. لا يمكننا العودة إليها إلا إذا قلنا ربما radius يساوي 2.2. إنها مجرّد قيمة راكدة في الذاكرة، وقد يجمعها لاحقًا — أو يستردّها — جامع القمامة (Garbage Collection) أو ما شابه ذلك. لكننا الآن لا يمكننا العودة إليها.
إذن ما قيمة area في نهاية هذه الأسطر؟ حسنًا، وفق هذا، فهي 15.1976. إذن إنها تستخدم القيمة القديمة 2.2 للقيمة radius. وهذا مقبول لأن البرنامج لم يطلب — ولم يُكتب له سطر يقول: أعِد حساب area بعد أن غيّرنا radius. إنه يتبع، ببساطة، سطرًا سطرًا. إنه لا يعرف أن «يا ترى، إذا غيّرتُ radius، فقد يريد المستخدم أن تتغيّر area». إنه لا يقيم هذه الروابط. إنه يتبع التعليمات فقط. وهذا مقبول. وإذا أردنا أن تتغيّر area، لاستطعنا نسخ هذا السطر ولصقه بعد أن غيّرنا radius. وعندها تتغيّر area أيضًا. هل هذا مفهوم؟ هذه نوعًا ما جزء مهم من هذه المحاضرة. حسنًا، رائع.
إذن الفكرة الكبرى هنا إن الأسطر تُقيَّم واحدًا تلو الآخر. نحن لا نتخطّى شيئًا. ولا نكرّر الأشياء. هذا سنتعلّمه لاحقًا. لكن في الوقت الحالي: سطرًا سطرًا. إذن إليكم «جرّب بنفسك» صغيرًا. هذه الأسطر الثلاثة تُنفَّذ بالترتيب. ما قيمتا المتغيّرين meters وfeet عند كل سطر؟ إذن في السطر الأول، ما قيمة meters بعد تنفيذ السطر الأول؟ 100. وماذا عن feet؟
إذن في نهاية السطر الأول، لا توجد قيمة لـfeet بعد. وماذا بعد السطر الثاني؟ 328.08. صحيح؟ وماذا عن قيمة meters؟
الحضور:
آنا بيل:
ما زالت 100. وماذا عن بعد السطر الثالث؟ أنا أُغيّر meters إلى 200. بالضبط، نعم. meters تساوي 200، لكن feet ما زالت 328.08.
وهذا شيء أودّ أن أريكم إيّاه اليوم. وسنستخدم Python Tutor كثيرًا في المستقبل. Python Tutor موقع مفيد يتيح لك أن تمشي عبر شيفرتك — أن تتنقّل فيها خطوة بخطوة.
في كل سطر تنفّذه، يمكنك أن ترى قيم كل المتغيّرات في الشيفرة. إنها أداة تنقيح (Debugging) مفيدة جدًا. آمل أن تجرّبها اليوم، وقد جرّبها يوم الاثنين، ربما، لتمرينات الإصبع إن واجهتك صعوبة. ويمكنك استخدامها في الاختبارات القصيرة تساعدك على التنقيح. لكن أستطيع فقط أن أريك إياها. إنها بسيطة إلى حد ما هنا لأنها مجرد خطوة بخطوة.
صفحة المصدر 15
آنا بيل: إذن نمشي خطوة بخطوة. الأحمر هو السطر الذي سأُنفّذه. الأخضر هو السطر الذي نفّذته للتو. إذن لقد نفّذتُ للتو meters يساوي 100. فهنا لدي متغيّر meters قيمته 100. الخطوة التالية. إذن لقد نفّذتُ للتو feet يساوي هذه. إذن لدي الآن متغيّر اسمه feet قيمته 328.08. وmeters ما زالت 100. ثم meters تساوي 200، وfeet بقيت 328.08.
إذن من الواضح أن هذا برنامج بسيط جدًا لتشغيل Python Tutor عليه، لكن يمكنك أن تتخيّل استخدامه في إعدادات أعقد. ماذا لو جربنا واحدة أخرى؟ وهذه أمثلتي الأخيرة. أريدكم أن تحاولوا كتابة برنامج يبدّل قيمتَي x وy.
إذن في الأصل — وسأرسم هذا، مخطط الذاكرة سريعًا: لدينا x مربوط بـ1. وy مربوط بـ2. وما أريد فعله، من دون أن أقول x يساوي 2 وy يساوي 1، هو أن أبدّل القيمتين. أريد أن يرتبط x بـ2 ويرتبط y بـ1، لكن باستخدام أوامر من هذا النوع فقط.
إذن الشيفرة هنا بها خلل. أي أنها خاطئة. فيها خطأ. حسنًا، لنمشِ خطوة بخطوة — لنمشِ خطوة بخطوة قليلًا في كل مرة. y يساوي x. ماذا أفعل حين يكون y يساوي x هنا؟ نعم، بالضبط، y ستنتقل من 2 إلى 1. والآن ماذا يحدث حين أفعل x يساوي y؟ نعم، x تبقى كما هي.
سطري الأول، y يساوي x، فقد الارتباط بالقيمة 2. والآن أصبح كل شيء مشوّشًا لأنني لا أستطيع استعادتها. إذن بدلًا من ذلك — فإذا لم تفهم هذا، يمكنك الضغط على Python Tutor وتمشي خطوة بخطوة بنفسك. لكن كيف يمكننا إصلاح هذا؟
الحضور:
أن ننشئ متغيّرًا ثالثًا.
آنا بيل:
أن ننشئ متغيّرًا ثالثًا؟ نعم، هذه فكرة عظيمة. نعم، يمكننا إنشاء متغيّر ثالث. إذن x تساوي 1، وy تساوي 1 — أقصد y تساوي 2. إذن يمكننا إنشاء متغيّر ثالث. إلى أي قيمة تريدون أن يُساوي المتغيّر؟ x أم y؟ نعم، أيًّا منهما. جعلتها y، فلنفعل y. إذن هنا لدي متغيّر مؤقّت اسمه temp، وجعلته يساوي 2. والآن ماذا يمكنني أن أفعل؟ أيّهما يمكنني أن أعيد إسناده الآن؟ x يساوي y أم y يساوي x؟
بالضبط، y يساوي — إن فعلت x يساوي y، فأنا أفقد ارتباطي بـ1، فيصبح مشوّشًا مرة أخرى. إذن y يساوي x مقبول. إذن سأفقد ارتباط y بالقيمة 2 وأربطها بـ1. والآن ماذا أفعل؟ نعم، الآن يمكنني بأمان أن أُعيد إسناد x إلى temp. فبإمكاني أن أقول x يساوي temp لأن temp يشير إلى 2. وأريد أن أجعل x يشير إلى 2 أيضًا.
إذن فيما يخص الشيفرة، هذا هو المخطط نوعًا ما. لكن يمكننا كتابة الشيفرة. إذن لا — لنرَ. نحن لا نكتبها هنا، لكن إن أردتَ فيمكنك كتابتها هنا بنفسك. أو يمكننا أن نفعلها معًا. إذن x يساوي — أوه. x يساوي 1، وy يساوي 2. ثم كان لدينا temp. أردنا أن نُسنده إلى ما كانت عليه قيمة y. فنقول temp يساوي y.
وإن أردتَ التحقّق من قيم المتغيّرات، يمكنك فقط أن تستدعي الأسماء. إذن x تساوي 1، وy تساوي 2، وtemp يجب أن تساوي ما كانت عليه y، أي 2. حسنًا، جيد حتى الآن. إذن أظن أنني عند هذه الخطوة الآن، صحيح؟ لقد أنشأتُ هذا للتو. وآخر شيء أحتاج أن أفعله هو أن أفقد ارتباط x بما كانت عليه temp.
إذن أريد أن أفعل هذه العملية هنا، أي أريد أن أُسند x لتكون مساوية لـtemp. إذن الآن x تساوي 2، وy تساوي 1. وماذا فعلتُ؟ نعم، هذا يحدث أحيانًا. يمكننا فقط أن نبدأ من جديد. إذن y يساوي temp. آسف. temp يساوي y. y يساوي x.
صفحة المصدر 16
آنا بيل: y تساوي واحد. وx تساوي 1. ثم x يساوي temp. y تساوي واحد، وx تساوي 2. إذن لا بأس إن أخطأتُ. ستُخطئ. يمكننا في هذه الحالة بالذات أن نبدأ من جديد من خلال إعادة تعريف متغيّراتنا وتجربة الأمر من جديد كله. فهذا هو الغرض من الصدفة (Shell). هذا ما أستخدمه لها. هذا ما سنستخدمها له في المستقبل، لمجرّد فعل أمور سريعة مثل هذه، وكذلك أمور مثل التحقّق من الأنواع والأوامر الأخرى التي تعلّمناها في وقت سابق.
حسنًا، إذن هل من أسئلة قبل أن نلخّص؟ هل كان كل هذا بالسرعة المناسبة أم كان سريعًا أكثر من اللازم؟ أم كان مقبولًا؟ حسنًا، جيد. رفع الإبهام يعني الموافقة. إذن لنُجرِ خلاصة سريعة. رأينا أننا يمكننا إنشاء برامج بالتعامل مع الكائنات (Objects). أنشأنا كائنات في Python. ورأينا أن الكائنات لها نوع معيّن. النوع الذي للكائن يخبر Python بما يمكن فعله بذلك الكائن.
ويمكننا أن ندمج الكائنات في تعبيرات. وهذه التعبيرات تُقيَّم — أي تغلي — إلى قيمة واحدة معيّنة. ويمكن تخزين الكائنات أو القيم في متغيّرات. وهذه المتغيّرات تتيح لنا الوصول إلى هذه القيم بأسماء أجمل لاحقًا في برنامجنا. وبذلك نتمكّن من كتابة برامج أكثر ترتيبًا ووضوحًا.
إذن علامة المساواة — أريتكم بعض الفروق بين الرياضيات وعلوم الحاسوب. كانت علامة المساواة فرقًا بارزًا. علامة المساواة في الرياضيات تقريرية (Declarative)، وعلامة المساواة في علوم الحاسوب إسناد (Assignment). أنت في الحقيقة تقول: هذا مرتبط بهذا. ونحن لا نُجري أي نوع من المساواة في علوم الحاسوب.
ونعم، الحاسوب يفعل ما تطلبون منه أن يفعله. هذه هي الفكرة الكبرى هنا. سطرًا سطرًا، ينفّذ ابتداءً من الأعلى، سطرًا سطرًا. حتى الآن لم نرَ أي مواضع يتّخذ فيها الحاسوب قرارًا. لكن في المحاضرة القادمة سنرى كيف يمكننا أن نُدخل نقاط قرار في برامجنا لتنفّذ الحاسوب مجموعة شيفرة أو مجموعة أخرى.
حسنًا، إذن هذا نهاية محاضرة اليوم. شكرًا لكم جميعًا على الحضور. سأراكم يوم الاثنين.