التفريغ الكامل للمحاضرة 2: السلاسل النصية والإدخال والإخراج والتفرّع
التفريغ الكامل للمحاضرة 2: السلاسل النصية والإدخال والإخراج والتفرّع
المصدر الأصلي: التفريغ الإنجليزي الرسمي بصيغة PDF، المرتبط بـصفحة المحاضرة والفيديو الرسمي.
النسبة والترخيص: آنا بيل (Ana Bell)، مقرر 6.100L: مقدمة في علوم الحاسوب والبرمجة باستخدام Python، خريف 2022، معهد ماساتشوستس للتكنولوجيا، MIT OpenCourseWare. هذه ترجمة وتكييف عربي غير رسمي، ولا تعني تأييد MIT أو اعتماده. الأصل المشمول والترجمة تحت رخصة CC BY-NC-SA 4.0: الاستخدام غير التجاري، مع النسبة ومشاركة التكييفات بالمثل. لا تشمل الرخصة تلقائيًا المواد الخارجية أو المستثناة من ترخيص المصدر.
منهج المطابقة: ترجمة التفريغ الرسمي المستخرج من PDF، لا الملاحظات ولا كلام مستنتج من الشرائح. عناوين صفحات المصدر تطابق الصفحات العشرين غير الفارغة المفصولة بمحرف فصل الصفحة في الاستخراج. حُفظ ترتيب الفقرات وأدوار المتحدثين والتكرار والتصحيحات والإشارات الصوتية. المعرّفات والتعبيرات البرمجية باقية، وأي توضيح تحريري موسوم بأنه ملاحظة مترجم.
صفحة المصدر 1
MITOCW | 6.100L Lecture 2 Multi.mp4
[صرير]
[حفيف]
[نقر]
آنا بيل: لنبدأ محاضرة اليوم. سننظر في ثلاثة موضوعات مختلفة. أولًا نوع كائن جديد يسمى سلسلة نصية (String). ذكرنا هذه الكلمة باختصار في المحاضرة الماضية. ثم نرى كيف نكتب برامج تبدأ بأخذ إدخال من المستخدم وعرض مخرجات عليه.
وأخيرًا سنكتب برامج أكثر إثارة قليلًا، تتخذ قرارات بناءً على قرارات نضعها نحن فعلًا في الشيفرة، لا قرارات تلقائية بل أشياء نبرمجها داخل برامجنا.
لكن قبل الانتقال لهذه الموضوعات أردت مراجعة سريعة لما تعلمناه، لنتأكد أننا جميعًا على الصفحة نفسها. قدّمنا فكرة الكائن (Object)، ولكل كائن في Python نوع محدد (Type). والنوع يخبر Python بالأشياء المسموح فعلها بهذا الكائن.
وتحدثنا عن إمكان إسناد الكائنات إلى متغيرات (Variables). تربط هذه المتغيرات أساسًا اسمًا بالكائن في الذاكرة. ويمكن أيضًا بناء تعبيرات (Expressions) بدمج الكائنات.
وقد تكون التعبيرات مما رأيناه في الرياضيات، بأقواس وبصيغة كائن ثم معامل (Operator) ثم كائن. أو أشياء مثل هذا، مما قدّمته البرمجة. إنه تعبير لكنه مختلف؛ أقرب لأمر. أطلب من Python إجراء هذه العملية: ما الكائن الذي يعود منها؟
سأراجع اليوم مخطط الذاكرة الصغير الذي بدأنا رسمه في المحاضرة الماضية، وسأستخدمه أيضًا. هذه بعض الأسطر التي كتبناها. أنشأنا — كتبنا سطرًا ينشئ كائنًا قيمته 3.14، عدد فاصلة عائمة (Float) في الذاكرة. والاسم الذي أعطيناه له pi، مجرد الاسم، P-I.
radius = 2.2 عبارة إسناد (Assignment) أخرى. تربط اسم radius بالقيمة 2.2 في الذاكرة. وبعد إنشاء المتغيرات نستدعي أسماءها لنخبر Python بأن يجلب القيم من الذاكرة. حين يرى pi * radius**2 فالمعنى أن يأخذ pi ويضربها في مربع radius.
خلف الكواليس، يجلب Python القيمة 3.14 ويستبدل بها pi، ويجلب 2.2 ويستبدل بها radius، ويجري العملية بحسب قواعد الأولوية. ثم يصبح التعبير الموجود يمين علامة المساواة قيمة. تُنشأ ككائن جديد في الذاكرة هنا، ويُربَط باسم area. هذا بالضبط معنى عبارة الإسناد.
صفحة المصدر 2
آنا بيل: نستطيع فعل شيء كهذا في Python ولا نستطيعه في الرياضيات. رياضيًا سيقول التعبير أساسًا 0 = 1. لكن في Python لا مشكلة، لأننا نقيّم ما على يمين علامة المساواة. نقول: اجلب قيمة radius، وهي 2.2، وأضف إليها 1 فتصبح 3.2، وأنشئ هذا الكائن في الذاكرة. هنا كائن جديد كليًا، 3.2، ثم أُسنده إلى الاسم radius.
فقدت الارتباط بـ2.2 الأصلية وأعدت ربط الاسم بـ3.2. لا نعدّل كائنات الذاكرة؛ ننشئ كائنات جديدة كلما أجرينا مثل هذه العمليات. سنرى تعديل الكائنات في المستقبل البعيد.
وللاكتمال، إذا كان لدينا سطر var = type(5*4)، يراه Python تعبيرًا أيضًا، ولذلك له قيمة. يمين المساواة يقول: سأقيّمه تدريجيًا؛ ما 5 في 4؟ 20. وما نوع 20؟ عدد صحيح (Integer).
إذن يُقيَّم الطرف الأيمن إلى عدد صحيح. وسأربط هذه القيمة، int، بالاسم var. إنه اسم متغير آخر، وقيمته int، نوع كائني، وهذا غريب قليلًا. حتى الآن وضعنا أعدادًا فقط في الذاكرة، لكن نستطيع وضع أي نوع كائن.
لننتقل قليلًا إلى نوع الكائن الجديد، السلسلة النصية. هي تسلسل من محارف حساسة لحالة الأحرف (Case Sensitive). يمكن أن تكون أي شيء: أحرفًا صغيرة وكبيرة، وأرقام لوحة المفاتيح، ورموزها الخاصة. حتى Enter لبدء سطر جديد أو Tab لهما محرف خاص مرتبط بهما.
نخبر Python بأننا ننشئ كائنًا نصيًا بإحاطة المحارف بعلامتي اقتباس. حين يراهما يعرف أنه ينشئ سلسلة نصية. هنا أنشئ كائنًا فيه الحرف الصغير m ثم e، وهنا كائنًا فيه y ثم o ثم u الصغيرة.
هذه الكائنات أشياء في Python. سنعطيها مقبضًا، ارتباطًا بأسماء متغيرات أنسب: a وz. في مخطط الذاكرة تبدو محارف "me" مرتبطة بالمتغير a، كما رأينا من قبل.
ما الذي نستطيع فعله بالسلاسل النصية؟ من العمليات الشائعة جدًا وصلها (Concatenation) أو تكرارها. لن أضع z في الذاكرة هنا؛ تخيلوا شكله. لكن لننشئ متغيرًا b يساوي أحرف myself.
ماذا إن وضعنا معامل الجمع بين السلسلتين؟ يخبر Python بأن يأخذ السلسلتين ومحارف كل منهما ويجمعها لتشكيل كائن جديد واحد من هذه الأحرف جميعًا. إذن c = a + b إسناد آخر.
على اليمين تعبير، معامل جمع بين كائنين. يجمع me، أحرف c، مع myself، أحرف b، وينشئ كائنًا جديدًا أعطيه المقبض أو الارتباط c. ومن الآن، كلما أردت سلسلة المحارف هذه، myself، يكفي استدعاء c في البرنامج؛ إنه اسم المتغير الذي أعطيتها.
ملاحظة مترجم: ورد في الكلام ذكر
cبدلa، وmyselfعند وصف الناتج. حُفظت هذه الألفاظ كما في التفريغ؛ وفق القيم المذكورة يكون ناتجa + bهو"memyself".
صفحة المصدر 3
آنا بيل: لاحظوا أنه لم يُدرج مسافة. لم يصنع me ثم مسافة ثم myself، لأننا لم نطلبها. لو أردناها لوضعناها بأنفسنا. يمكننا الوصل — أي بناء تعبير أكبر يصل a بمسافة وبـb. يعطيني كائنًا جديدًا كليًا، سلسلة me ثم مسافة ثم myself، مرتبطًا باسم d. هل هذا واضح حتى الآن؟ مفهوم؟ حسنًا.
هذا هو الوصل: يأخذ محارف السلسلتين ويجمعها في كائن جديد. وماذا عن النجمة؟ تحدثت باختصار عن تكرار شيء في المحاضرة الماضية. يعمل معامل النجمة بين سلسلة نصية وعدد.
لا يعمل بين سلسلتين، ولا بين أشياء كهذه. يعمل بين سلسلة وعدد في أي ترتيب: عدد ثم سلسلة، أو سلسلة مضروبة في عدد.
مجددًا، هذه عبارة إسناد. نعرف أولًا إلى ماذا يُقيَّم الطرف الأيمن. a * 3 تعني تكرار تسلسل المحارف me، لأن هذا هو a، ثلاث مرات. سينشئ هذا السطر كائنًا جديدًا me وme وme.
وتخبر علامة المساواة بأن يربطه بالاسم silly. كلما أردت جلب هذا التسلسل من الذاكرة، أكتب silly في البرنامج، فيجلبه تلقائيًا.
لنحل تمرينًا سريعًا للتأكد. بينما تفكرون بما يفعله، أبدأ كتابته في وحدة التحكم، أو اكتبوه بأنفسكم للتأكد. ستكون b النقطتين الرأسيتين، وc قوس الإغلاق. هنا b تساوي ":" وc تساوي ")".
لا يلزم إجراء العمليات دفعة واحدة. نجرّب 2 * c: يكرر قوس الإغلاق مرتين. ثم b + 2 * c يعطينا نقطتين وقوس إغلاق وقوس إغلاق، وجهًا سعيدًا جدًا.
التالي: f هي a، وg في الواقع مسافة ثم b. يوجد محرف مسافة، وهذا خادع قليلًا. وh هي 3. هل هي العدد 3 أم السلسلة 3؟ بالضبط، السلسلة 3. فما f + g؟ مجددًا نجرب على أجزاء. إنها a ثم مسافة ثم b، صحيح. a وحدها، وg مسافة ثم b.
ما int(h)؟ نعم، 3 فقط. وما نوع 3؟ int، بالضبط، حوّلتها للتو إلى int. إذا كانت f هي a، وg مسافة b، وh السلسلة 3، فإن f + g هي a مسافة b، شيئًا فشيئًا. وh هذه السلسلة 3؛ تحويلها لعدد صحيح يعطيني العدد 3 فقط.
يمكن إحاطة كل منها بأمر type لمعرفة النوع الدقيق، لكننا نميّزه فورًا. إذا فعلنا f + g مضروبًا في int(3) — عفوًا، int(h)، وهو 3 — سيكرر a مسافة b ثلاث مرات: واحدة واثنتان وثلاث.
ماذا يحدث لو نسيت التحويل لعدد صحيح واستخدمت h فقط؟ خطأ، بالضبط. ليست الأخطاء مخيفة، بل مفيدة حين تعتادونها. هذا خطأ نوع (Type Error): شيء غير صحيح في أنواعنا. لا يمكن ضرب تسلسل، أي عدد صحيح — أو سلسلة، تسلسل — في غير عدد صحيح. حسنًا.
صفحة المصدر 4
آنا بيل: ما العمليات الأخرى على السلاسل؟ هناك أشياء لم نرها مع الأعداد في المحاضرة الماضية. من الشائع الحصول على طول السلسلة (Length)، ليخبرنا بعدد محارفها.
إن قلنا s = "abc"، أنشئ سلسلة محارفها a وb وc وأعطيها اسم s. كلما قلت s في البرنامج يستبدلها Python بسلسلة abc.
يمكن إحاطة s بهذا الأمر len. إنه تعبير؛ يقرأه Python ويقيّمه إلى قيمة واحدة، أي يستبدل التعبير بقيمة واحدة. كم محرفًا في سلسلتي؟ يصبح len(s) العدد 3.
يمكن في البرنامج وضع إسناد آخر هنا، chars = len(s). هذا تعبير يُقيَّم إلى 3، كما في السطر السابق؛ فكأن السطر يقول chars = 3.
هذه عملية بسيطة. سننتقل لأخرى أكثر تفصيلًا قليلًا تتطلب تذكر صياغة Python. يمكننا جلب محارف منفردة عند مواضع مختلفة. يسمى ذلك التقطيع (Slicing).
الصياغة، أو طريقة فعل ذلك، تستخدم الأقواس المربعة. ترونها هنا؛ هذه ببساطة طريقة Python. إذا كانت s هي abc، نخبر Python باستخراج محرف عند موضع معين بالفهرسة (Indexing) داخل السلسلة.
تبدأ الفهرسة في Python ولغات البرمجة الحديثة من 0. نعدّ من 0 في البرمجة وعلوم الحاسوب. ففهرس المحرف الأول، a، هو 0، وفهرس b هو 1، وفهرس c هو 2.
نسأل: ما المحرف في الموضع الأول؟ لكن بلغة علوم الحاسوب نقول: المحرف عند الفهرس 0. المحرف عند الفهرس 1 هو في الموضع الثاني، وهكذا. نبدأ العدّ دائمًا من 0 عند فهرسة سلسلة.
s[0] هو ما نسمي به هذا السطر. تعبير Python آخر، يبدو مختلفًا عن السابق، لكنه كله يُقيَّم إلى قيمة محددة: المحرف عند ذلك الفهرس.
لأريكم شكله هنا، إذا كانت s تساوي abc، نكتب s[0]. يُقيَّم التعبير إلى a وحدها. و s[1] إلى b، و s[2] إلى c.
أما s[3] فيسأل عن المحرف في الموضع الرابع. لكن abc لها ثلاثة مواضع فقط، فيعطينا الخطأ الثاني في المقرر، خطأ فهرس (Index Error). وهو شائع عند البدء ببرامج أعقد؛ معناه أساسًا أنك فهرست بعيدًا جدًا داخل القائمة، يمينًا أو يسارًا.
يمكن أيضًا الفهرسة بمؤشرات سالبة. لجلب المحرف الأخير في أقصى اليمين نستخدم الفهرس سالب 1. إنها طريقة مريحة جدًا لجلب آخر محرف.
صفحة المصدر 5
آنا بيل: نسأل s[-1] فيجلب Python المحرف الأخير تلقائيًا. لا نحتاج تعبيرًا مثل len(s) - 1؛ فهذا يعطي c أيضًا.
هنا أدرجت التعبير len(s) - 1 في الفهرس نفسه، وهذا جائز. يقيّم Python من الداخل للخارج ومن اليسار لليمين. قيّم التعبير إلى 2، فأصبح السطر يسأل عن s[2]، ونعرف أنها c.
أحد الحضور: لماذا جعل سالب 1 هو c؟
آنا بيل: لأن الفهارس السالبة تبدأ العدّ من الجانب الأيمن؛ إنه اصطلاح Python. لذا s[-4] يعطينا خطأ أيضًا لأننا فهرسنا بعيدًا جدًا يسارًا. لا شيء هناك.
نستطيع الفهرسة لجلب محارف منفردة: أقواس مربعة وفهرس المحرف المطلوب. ويمكن أيضًا التقطيع لجلب سلاسل جزئية (Substrings). بدل محرف واحد، نطلب سلسلة تبدأ بفهرس وتصل إلى آخر، وقد تتجاوز بعض المحارف. يمكن أخذ كل محرف، أو أخذ محرف وترك التالي، أو نمط آخر.
الصياغة شبيهة بتقطيع محرف واحد، مع اختلاف بسيط. التشابه وجود الأقواس المربعة؛ والاختلاف إعطاء ثلاثة أعداد بينها، مفصولة بنقطتين رأسيتين.
الأول فهرس البداية: أين تبدأ السلسلة الجزئية؟ والثاني فهرس التوقف: نأخذ المحارف بدءًا من الأول إلى الأخير دون تضمين الأخير.
والخطوة (Step) تعني كم محرفًا نتجاوز. إذا كانت 1، نأخذ كل محرف. وإذا كانت 2، نأخذ واحدًا ونترك التالي. وإذا كانت 3، نتجاوز محرفين، وهكذا.
هناك تركيبات عديدة للأعداد الثلاثة ستعتادونها مع التمارين. الآن لا يضر إعطاء البداية والتوقف والخطوة دائمًا؛ هذا جيد تمامًا.
لكن الشائع، إن كنتم ستأخذون كل محرف، حذف جزء الخطوة. إذا أعطيتم عددين فقط، عدد ثم نقطتان ثم عدد، يعرف Python تلقائيًا أن الخطوة افتراضيًا 1؛ فلا يتجاوز شيئًا. وإذا أعطيتم عددًا واحدًا دون نقطتين، نعود للشريحة السابقة، أي جلب عنصر واحد.
أعرف أن هذا مربك قليلًا. سنرى مثالًا في الشريحة التالية. لكن عليكم التدرب في الصدفة بالمثال التالي، آمل أن تفعلوا في البيت لتتأكدوا أنكم تفهمون. إذا خطر سؤال مثل: ماذا لو وضعت هذا العدد أو ذاك؟ ضعوه في الصدفة وانظروا.
لنرَ بعض الأمثلة. كيف نقطّع للحصول على سلاسل جزئية؟ لنفترض السلسلة الأطول abcdefgh. عند التقطيع أول ما ننظر إليه هو الخطوة.
صفحة المصدر 6
آنا بيل: هل هي موجبة أم سالبة؟ إن كانت موجبة — وتذكروا أن حذفها يعني افتراضيًا موجب 1 — نعمل من اليسار لليمين، اتجاه قراءتنا. وإن كانت سالبة، نعمل من اليمين لليسار.
ماذا عن s[3:6]؟ الخطوة موجب 1 فنذهب يسارًا ليمين. نبدأ عند الفهرس 3، أي d. نأخذ D، ونجلب السلسلة من d إلى المحرف عند الفهرس 6 دون تضمينه، h — g، عفوًا g.
نأخذ d وe وf. نبدأ عند 3 ونصل إلى 6 دون تضمينه، ونأخذ كل محرف لأن الخطوة 1. ماذا لو كانت 2؟ الشيء نفسه، لكن الخطوة 2.
مجددًا الخطوة موجبة، فنذهب يسارًا ليمين. نبدأ عند 3 ونأخذ d، وننشئ كائنًا جديدًا فيه d. نتجاوز e لأن الخطوة 2، ونأخذ f فقط. وصلنا إلى العنصر عند الفهرس 6 دون تضمينه.
وهناك أشياء أخرى، حيل ربما تسمّونها. إذا وضعنا نقطتين فارغتين هنا، فمعناهما إنشاء الكائن نفسه مجددًا؛ يُقيَّم إلى abcdefgh. وإذا فعلنا [::-1] فهي صياغة مختصرة لجلب السلسلة معكوسة: hgfedcba. السلسلة الأصلية نفسها لكن إلى الخلف.
ويمكن فعل شيء مثل 4 إلى 1 بخطوة سالب 2. الخطوة سالبة، فنذهب يمينًا لليسار. نبدأ عند الفهرس 4 فنأخذ e، ونتجاوز محرفًا في كل مرة: لا نأخذ d، بل c. وننزل إلى المحرف عند الفهرس 1 دون تضمينه، فنتوقف هنا.
أخذنا بالترتيب e ثم c، فيُقيَّم التعبير كله إلى ec. نعم، سؤال؟
أحد الحضور: لماذا تجاوزتِ d؟
آنا بيل: لأن الخطوة 2. عندما تكون 1 نأخذ كل محرف، وعندما تكون 2 نتجاوز كل محرف تالٍ. نعم؟
أحد الحضور: في الأول، s من 3 إلى 6، لماذا لا تُضمَّن g؟
آنا بيل: بحسب التعريف فقط: نصل إلى التوقف ولا نضمّنه. نصل للمحرف عند 6 دون تضمينه. هذا تعريف التقطيع في Python.
أحد الحضور: مثل محرف يبدأ إلى stop - 1 مع تضمينه [غير مسموع].
آنا بيل: إلى stop - 1 مع تضمينه تعني إلى 5 مع تضمينه، نعم بالضبط. مجددًا إن لم تكونوا متأكدين مما يفعله أمر، جربوه دائمًا في وحدة التحكم، الصدفة. إليكم فرصة لذلك: سلسلة s، ABC d3f. سأكتبها فعلًا: ABC ثم مسافة — هنا مسافة — 3d، ماذا أفعل؟ آه، d3f. ثم مسافة أخرى ثم ghi.
صفحة المصدر 7
آنا بيل: ما نتيجة الأول؟ 3 ثم نقطتان ثم len(s) - 1. سأكتب الفهارس لكم أيضًا. ما البداية؟ نعم، المسافة، بالضبط. ستكون مسافة صغيرة. وما len ناقص 1؟
أحد الحضور: هذا الطول، صحيح؟ ليس [غير مسموع].
آنا بيل: نعم، ما الطول؟ كم محرفًا هنا؟ 11، وناقص 1 يساوي 10. عندما يكون التوقف 10 نصل إلى المحرف عند 10 دون تضمينه، أي إلى h هنا. نأخذ مسافة ثم d3f ثم مسافة ثم gh ونتوقف. نعم.
أحد الحضور: لماذا نبدأ التالي من 0 مجددًا؟ إنه فقط —
آنا بيل: اصطلاح.
أحد الحضور: حسنًا.
آنا بيل: علوم الحاسوب، البرمجة. باستثناء MATLAB؛ أظن أنها ما زالت تبدأ من 1. أسئلة أخرى عن هذا؟ واضح؟ التالي s[4:0:-1]. ما العنصر عند 4؟ d، نعم. نأخذ d. هل نتحرك يمينًا أم — إلى اليمين أم اليسار؟
أحد الحضور: اليسار.
آنا بيل: بالضبط. نصل للمحرف عند 0 دون تضمينه. نأخذ d ثم المسافة ثم c ثم B. هل آخذ a؟ لا، بالضبط. فقط D ومسافة وCB. نعم؟
أحد الحضور: إن أردنا تضمين A، هل تكون القيمة الثانية سالب 1؟
آنا بيل: إن أردت تضمين A فأظن أنك تحتاج شيئًا آخر. لا يمكن الوصول إلى سالب 1 لأنها هذا هنا. سؤال جيد، عليّ التجربة واللعب به. لكن لتضمينها ربما تجعل — عفوًا، تفضل — ربما تترك جزءًا فارغًا مع النقطتين، وهو افتراضيًا يعني البداية والنهاية. لكن عليّ تجربته، نعم.
والأخير؟ 6 ثم نقطتان ثم 3. ما العنصر عند 6؟ نجحت النقطتان الفارغتان؟ ممتاز، شكرًا للتجربة. نعم تعملان إن أردت جلب B. حسنًا، s[6:3]، ما العنصر عند 6؟
أحد الحضور: F.
آنا بيل: F، رائع. ونعمل يمينًا أم يسارًا؟ يمينًا. نبدأ هنا لكن يجب السير في هذا الاتجاه. وما فهرس التوقف؟ نعم، ليس هناك؛ إنه خلفنا. هذا الأخير إذن سلسلة فارغة (Empty String). ويمكن — يمكن أن نجرب شيئًا آخر أيضًا.
إذا كانت لدينا ABC، وبدأت الفهرسة من 2 وتراجعت إلى 0، تعطيني سلسلة فارغة. وهي علامتا اقتباس لا شيء بينهما. أي لم نأخذ أي محرف في هذه الحالة. واضح؟
أحد الحضور: هل هذا صالح؟
آنا بيل: صالح؛ لا توجد محارف بين f وخلف f. نعم. أذكر الآن أن السلاسل النصية كائنات غير قابلة للتغيير (Immutable).
صفحة المصدر 8
آنا بيل: كثير من الكائنات التي رأيناها غير قابلة للتغيير فعلًا؛ لا يمكن تعديلها بعد إنشائها. رأينا هذا: في مخططات الذاكرة حين أنشئ كائنًا جديدًا، مثل النسخة النصية من عدد صحيح أو أحوّل عدد فاصلة عائمة إلى صحيح، لا أغيّر الكائنات الأصلية؛ أنشئ مربعًا أخضر جديدًا في الذاكرة وأعيد إسناد الاسم.
سنرى لاحقًا كائنات قابلة للتغيير (Mutable)، يمكن تعديلها بعد إنشائها في الذاكرة. لكن الآن، كلما أجريتم تغييرًا على كائن كهذا — لا تستطيعون تغييره نفسه. للحصول على نسخة مختلفة ينشئ Python كائنًا جديدًا ويمكن إعادة إسناد المتغير إليه.
مثلًا إذا كانت car في الذاكرة مرتبطة بـs وأردت تغيير أول حرف إلى b، فهذا غير مسموح. لا يسمح Python بأن أغيّر الحرف عند الفهرس 0 إلى b. يمكن الحصول على نسخ جديدة من السلسلة بتعبير ما ينشئ bar التي قد تريدونها. لكن car تبقى في الذاكرة؛ نفقد ارتباطها فقط.
أسئلة عن السلاسل؟ إنها غالبًا نوع بيانات جديد، لم تعملوا به كما عملتم بالأعداد، فهي مختلفة قليلًا. مجددًا، سأل أحدهم: كيف نأخذ A إلى الخلف؟ جربوا في وحدة التحكم. يسعدني الإجابة ومساعدتكم على التجربة معكم، لكن هذا ما وُضعت وحدة التحكم لأجله، الصدفة هنا. لتجربة أشياء صغيرة سريعًا إن خطر لكم: ماذا لو هذا أو ذاك؟
لننتقل للإدخال والإخراج (Input/Output). البرامج التي نكتبها حتى الآن جامدة جدًا. لا أشياء كثيرة مثيرة نفعلها بها ولا تفاعل مع المستخدم. وحتى الآن عند محاولة إخراج أشياء، ربما تظنون أننا فعلنا ذلك بالفعل.
حين نكتب في وحدة التحكم 3 + 2، يعرض Python شيئًا في الصدفة. ربما هذه طريقة التفاعل مع المستخدم. لكنه ليس إخراجًا حقيقيًا. أسميه استراق النظر إلى قيمة التعبير. لو كتبتم مثل هذا التعبير في محرر الملفات، لن يطبعه Python فعلًا.
هذه الأشياء التي جربناها اليوم: أنشأنا سلاسل، وأخذنا طول s، وفهرسنا. كلما كتبنا التعبيرات في الصدفة، أعطانا Python القيمة تلقائيًا.
لكن إذا كتبتها كما هي في محرر الملفات على اليسار فلن يطبعها. هذا محرر الملفات، ومن الآن سنعمل بالملفات فقط. أشغّله بالزر الأخضر الصغير Run أو F5. حدث شيء في الصدفة، شُغّل برنامجي، ويقول إنه شغّل هذا الملف، لكن لا مخرجات. أين طول s؟ أين الفهارس السابقة؟
لأنها ليست مخرجات حقيقية. حين كتبنا في الصدفة كنا نجرب تعبيرات قصيرة تعطينا — لهذا أسميه استراق النظر للقيمة، لا إخراجًا حقيقيًا. إذا أردنا أن يرى المستخدم المخرجات، والصدفة طريقة عرض مخرجات الملف، فعلينا إخبار Python صراحة: أريد عرض ناتج هذا التعبير أو هذا الأمر. نفعل ذلك بأمر print.
صفحة المصدر 9
آنا بيل: إذا أخذنا التعبير المطلوب وأحطناه بأمر print، يعرض Python هذا الناتج فقط. تخيلوا لو كتبنا ملفًا يجري كل العمليات ويعرض كل المخرجات الوسيطة؛ لصار الملف — أو البرنامج — فوضويًا جدًا. لهذا يوجد أمر نحدد به صراحة ما نريد عرضه للمستخدم.
لطباعة طول s نضعه في عبارة print ونشغّل الملف. الشيء الوحيد المعروض الآن هو ما طلبت طباعته صراحة، 3. وإذا أردت طباعة هذا الناتج الآخر، نتيجة التعبير الآخر، أحيطه بعبارة طباعة فيطبعه أيضًا. أنا الآن مسؤولة عما أعرض للمستخدم.
كلما كانت لديكم عبارة print يطبع Python نتيجة التعبير ثم يبدأ سطرًا جديدًا. رأيتم عبارتين: واحدة حول len وأخرى حول s[-3]، ووضع كل نتيجة في سطر مختلف.
قد تريدون أحيانًا التعبيرات — أو نتائجها — في السطر نفسه. نستطيع وضع الكائنات المختلفة كلها داخل عبارة طباعة واحدة، ونفصلها بفواصل. هذا المثال هنا. يطبع Python كل الكائنات مهما كانت أنواعها، ويفصل بينها بمسافة.
هذا الكائن "the"، وهذا العدد 3، وهذه السلسلة "musketeers". طبعها في سطر واحد بمسافات بينها. هذا ما تفعله الفاصلة: تدرج المسافة تلقائيًا. لنفترض أننا لا نريد مسافة لأي سبب.
ماذا لو حاولنا وصل هذه الكائنات؟ رأينا أن الوصل لا يدرج مسافات، بل يدمج السلاسل. ونشغّل؟ لقد كشفت النتيجة أصلًا: خطأ، لكن لنره. خطأ نوع يقول لا يمكن وصل الأعداد الصحيحة بالسلاسل؛ الوصل للسلاسل فقط.
منطقي: هذه سلسلة وهذه ليست سلسلة، فلا يجوز، وهذه سلسلة. بدل وصل كائنات مختلفة علينا تذكر تحويل كل كائن غير نصي إلى سلسلة. هذا السطر كالسابق تمامًا إلا أن b، العدد الصحيح 3، تتحول الآن إلى سلسلة بإحاطتها بـstr. يحوّل العدد إلى سلسلة، ويسعد Python بوصل السلاسل الثلاث. هذا أساسًا ما قلته.
هذا الإخراج باستخدام print. وماذا عن الإدخال؟ نحصل عليه من المستخدم، لا مفاجأة، بأمر input. الصيغة عادة هكذا: الأمر وبين قوسيه سلسلة، ونريد عادة حفظ الإدخال في متغير.
ستشرح الشرائح التالية خطوة خطوة ما يحدث بهذين السطرين: text تساوي input مع "Type anything"، ثم أطبع 5 * text. عندما يرى Python input وسلسلة، يأخذ تلقائيًا السلسلة داخله. هنا أمر الإدخال، والسلسلة فيه "Type anything" ثم نقطتان ومسافة.
صفحة المصدر 10
آنا بيل: يضع Python هذه السلسلة في الصدفة ثم ينتظر. ينتظر أن يكتب المستخدم شيئًا ويضغط Enter. بمجرد الضغط، كل ما كتبه — لنقل "howdy" — يُحفَظ كسلسلة تستبدل عبارة input.
يمكن التفكير في input كتعبير غريب لأنه ينتظر أن يعطينا المستخدم شيئًا. لكن في النهاية يُستبدل بالنسخة النصية مما كتبه. يمكن كتابة أعداد أو أحرف أو محارف أو أي شيء؛ عند ضغط Enter يُحفَظ كل ذلك كسلسلة تستبدل input.
في الذاكرة يبدو ذلك هكذا: هذه سحابة الذاكرة، وهذا — هذا الكائن الذي أنشأته، المحارف نفسها التي كتبها المستخدم.
إذا كتب howdy، إلى ماذا ينتهي السطر؟ يصبح text = "howdy". وهذا ما رأيناه في الشريحتين السابقتين حين عملنا بالسلاسل؛ نسند المتغير ونربطه بهذه المحارف.
السطر التالي سهل: نطبع نتيجة تكرار text خمس مرات. يعرض print في الصدفة howdyhowdyhowdyhowdyhowdy، ما كتبه المستخدم خمس مرات.
مثال آخر: نطلب عددًا ونريد طباعة خمسة أضعاف ما يكتبه المستخدم. يأخذ num1 الإدخال مجددًا. نطلب كتابة عدد؛ عندما يرى Python ذلك يطبع "Type a number" ثم ينتظر. لنقل إنه كتب 3، فتُحفَظ كسلسلة "3".
مهما كتب المستخدم يُحفَظ كسلسلة، حتى إن كان عددًا؛ السلسلة التي تمثل ذلك العدد. عند Python هو محرف، وعندنا عدد، لكنه عند Python ما زال محرفًا. يصبح num1 في الذاكرة السلسلة 3، محرفًا واحدًا. ما شكل طباعة 5 * num1؟ أخبروني.
أحد الحضور: 33333.
آنا بيل: صحيح، 33333، لأننا نعمل بسلسلة لا بعدد صحيح. إن أردنا عددًا صحيحًا نحيط input بتحويل نوع (Cast). نستطيع دمج التعبيرات. هنا ندمج التحويل، الإدخال، مع عبارة الإدخال. يكتب المستخدم 3 مجددًا، والإدخال نفسه السلسلة 3، لكن السطر يصبح num2 = int("3"). فعلت هذا في الصدفة قبل قليل؛ عندما نحوّل عددًا — سلسلة — إلى int، يصبح ذلك العدد الصحيح.
تصبح num2 هي 3؛ ليست في الذاكرة السلسلة 3 بعد الآن، لأننا حوّلناها للعدد 3. وعند طباعة 5 * 3 نجري العملية الحسابية: 15.
لنجعلكم تبرمجون. سأعطيكم دقيقتين لكتابة برنامج تفاعلي يسأل المستخدم عن شيء ويطبع شيئًا له. سنطلب فعلًا، ثم أريد طباعة شيئين.
صفحة المصدر 11
آنا بيل: الأول: مهما كان الفعل الذي كتبه المستخدم، اكتبوا "I can whatever better than you" في سطر واحد. وفي التالي، بعبارة طباعة أخرى، اطبعوا الفعل خمس مرات. إن كتب run، تكتبون "I can run better than you" ثم في السطر التالي run run run run run.
طريقة تمارين «جرّب بنفسك» (You Try It) أن لديّ هنا مساحة، وقد كتبت التعليمات مسبقًا. عليكم ملء الشيفرة فقط. أعطيكم دقيقتين ثم نكتبها معًا باقتراحاتكم، ونرى إلى أين نصل، وسنكملها بالتأكيد — بالتأكيد معًا؛ لا يلزم أن تكملوها وحدكم. نعم؟
أحد الحضور: هل يفترض أن يكون فيه [غير مسموع]؟
آنا بيل: يفترض أن يكون هذا الملف لديكم. إنه ضمن ملف zip الذي نزّلتموه لليوم.
أحد الحضور: كيف — آه، ها هو.
آنا بيل: نعم. هل لدى أحد بداية؟ كيف أطلب إدخالًا من المستخدم؟ نعم؟
أحد الحضور: أظن السؤال كان [غير مسموع].
آنا بيل: نعم، هذا مناسب. وأضيف مسافة صغيرة بين النقطتين، أو أي رسالة طلب لديكم، كي لا يكون ما يكتبه المستخدم ملاصقًا للنقطتين أو نهاية السلسلة. بمجرد ذلك، سينتظر المستخدم — البرنامج — ويستطيع المستخدم كتابة شيء. ما الخطوة التالية؟ ما الأولى — كيف تستخدمون هذا الإدخال؟
أحد الحضور: ننفّذ print.
آنا بيل: نعم، لنطبع شيئًا. "I can" بين اقتباسين. نعم، "I can".
أحد الحضور: هل يمكن خارج الاقتباسين وضع question داخل [غير مسموع]؟
آنا بيل: نعم، نضع question، بالضبط. "I can" ثم question ثم فاصلة، لأنه كائن آخر ويسعدني وضع مسافة بينهما، ثم سلسلة أخرى "better than you". أوه، حسنًا، هكذا. ولا نحتاج كتابة البرنامج كله فورًا؛ نختبر هذا الجزء فقط. اختر فعلًا، run الذي أعطيتكم جيد. يبدو صحيحًا حتى الآن. نتابع الجزء الثاني. كيف أطبع الفعل خمس مرات؟ نعم؟
أحد الحضور: print ثم question مضروبًا في 5.
آنا بيل: print(question * 5). لنشغّل ونرَ. run. ليس تمامًا؛ تنقصني المسافات، لكنها بداية ممتازة. كيف أضيفها؟ نعم؟
أحد الحضور: أقواس، ثم الفعل زائد اقتباسين فيهما مسافة.
آنا بيل: نعم، داخل أقواس، نوصله بمسافة، بالضبط. وكل ذلك مضروبًا في 5. لنجرب. run. يبدو جيدًا جدًا. أريد ذكر تحسين واحد للبرنامج. إن نظرنا للمخرجات، فإننا نطبع فعلًا ثم مسافة: 1 و2 و3 و4، وحتى الأخيرة تُطبع مع مسافة في النهاية.
صفحة المصدر 12
آنا بيل: تحدٍّ لكم، والإجابة أسفل قليلًا — أزوّدكم بإجابات هذه التمارين — فكروا كيف تغيّرون عبارة الطباعة الأخيرة كي لا تكون هناك مسافة زائدة بعد آخر run. فكروا فقط، لا يلزم فعله الآن.
بما نعرفه الآن، نستطيع تطبيق الأفكار على مثال عددي أكثر. طريقة نيوتن (Newton's Method) تجد جذور كثيرة حدود (Polynomial) عدديًا بفكرة التقريب المتتابع (Successive Approximation). لا نستطيع كتابة الخوارزمية كاملة بما نعرفه، لكن نستطيع كتابة جزء مهم جدًا: الحصول على تخمين تالٍ بناءً على تخمين أولي.
لا تحتاجون فهم عمل الخوارزمية، لكن التخمين التالي يبدو هكذا. هذه الصيغة: التخمين الأصلي ناقص قيمة كثيرة الحدود التي نريدها عند ذلك التخمين، مقسومة على مشتقة (Derivative) الدالة عند التخمين نفسه.
هنا شيفرة تطلب إدخالًا: ما x التي نريد جذرها التكعيبي؟ ثم نسأل: بأي تخمين تريد البدء؟ يمكن طباعة التقدير الحالي مكعّبًا، guess مرفوعًا للقوة الثالثة. والتخمين التالي يتبع الصيغة هنا: التخمين الأصلي g الذي قرأته من الطالب — أو الإدخال — ناقص قسمة.
البسط هو f(g). الحاسوب لا يقيّم f من تلقائه؛ علينا كتابة الصيغة، الدالة، فعلًا. نريد قيمتها عند g، فهي g**3 - x، دالتنا هنا، مقسومة على المشتقة.
ومجددًا لا يحسب البرنامج المشتقة تلقائيًا؛ نخبره بها يدويًا. مشتقة g**3 - x هي 3*g**2، فنكتبها صراحة. والتخمين التالي هو تلك القسمة والطرح.
أحد الحضور: هل هذه دالة للمشتقة؟
آنا بيل: عفوًا؟
أحد الحضور: عفوًا، هل هذه دالة لمشتقة [غير مسموع]؟
آنا بيل: توجد حزم Python تسمح بذلك، لكن لأغراضنا سنكتبها صراحة هنا. نعم. هكذا تبدو الشيفرة، كما كانت هناك تمامًا. لو شغّلناها، ولنفترض أننا نريد الجذر التكعيبي لـ27 ونبدأ بـ5، تخبرني أن مكعب 5 هو 125، كبير جدًا طبعًا، وأن التخمين التالي 3.69.
هذا كل ما يفعله البرنامج. لا يأخذ التخمين التالي ليصنع آخر. لم نتعلم فعل ذلك بعد، لكن سنتعلمه في المحاضرتين المقبلتين.
وأريد ذكر السلسلة المنسّقة (f-string). أصبحت متاحة، أظن قبل سنتين، في Python 3.6. إنها طريقة أسهل بكثير لطباعة مزيج من نص حرفي ونتائج تعبيرات. إن كانت لديكم تعبيرات معقدة كثيرة لطبعها، فاستخدموا f-string هذه الأيام.
صفحة المصدر 13
آنا بيل: نعرف هذين السطرين الأولين؛ تعلمناهما في الشرائح القليلة الماضية. إذا أردتم هاتين القيمتين وطباعة أن هذا العدد الكبير يساوي نسبة كذا بالمئة من العدد الأصلي، فستلاحظون عند التشغيل أن الفاصلة تضع مسافة زائدة بين العدد وعلامة النسبة، وهذا لا يبدو جيدًا. حين تكون 3% نتوقع العلامة ملاصقة للعدد.
لكن الفاصلة تضيف مسافة فيبدو غريبًا. كان حلنا تحويل الأشياء إلى سلاسل. لكي تلصق علامة النسبة بالعدد، نصل هذا التحويل بالعلامة.
أما f-strings فتتيح ذلك كله دفعة واحدة؛ لا وصل ولا تحويل نفكر فيهما. هي حرف f ثم سلسلة طويلة، مزيج من تعبيرات وأشياء أريد طباعتها حرفيًا.
ما ليس داخل أقواس معقوفة يُطبَع حرفيًا: المسافة مسافة، ثم لاحقًا علامة النسبة والمسافة و«of percent»، كلها تُطبع كما هي. وما داخل الأقواس المعقوفة يُعتبر تعبيرًا في Python.
قبل الطباعة يقيّم Python num * fraction، ويعرف أنها متغيرات، ثم fraction * 100، ثم لاحقًا none. هذه متغيرات أو تعبيرات تُقيَّم قبل عرضها على الشاشة.
لاحظوا أننا ربما اضطررنا سابقًا لتحويلها إلى سلاسل لوصلها، لكن الآن لا، لأنها في هذه الصيغة الخاصة داخل أقواس f-string. تدربوا عليها. سأبدّل بين الطرق: هذه أحيانًا، والتحويل أحيانًا، وf-strings أحيانًا. لكن استخدموا f-strings متى أمكن؛ هذه الطريقة المفضلة في Python الآن.
الفكرة الكبرى، حتى مع f-strings، إمكان وضع التعبيرات في أي مكان. رأينا — وضعنا تعبيرًا — لا أذكر هنا — حيث فهرسنا، وضعنا تعبيرًا في الفهرس. والآن داخل عبارات الطباعة، والآن داخل f-strings. يمكن وضعها في أي مكان فعلًا، وهذا رائع ومتعدد الاستخدامات. يقيّمها Python ثم ينتقل للأسطر التالية.
الموضوع الأخير — عفوًا، أسئلة أخرى عن الإدخال والإخراج؟ واضح؟ آخر ما سنتحدث عنه اليوم، وربما قليلًا في المرة المقبلة، هو شروط التفرّع. برامجنا الآن خطية جدًا: أسطر شيفرة تُقيَّم واحدًا واحدًا. لا قفز ولا تكرار ولا نقاط قرار. القيم التي تحصلون عليها هي الموجودة في البرنامج فقط.
سنرى إضافة نقاط قرار. إذا كانت قيمة ما — قيمة متغير — أقل من قيمة آخر، نقيّم شيفرة، وإلا ننفّذ شيفرة أخرى. يمكن الآن تجاوز بعض الشيفرة بهذه الفكرة الجديدة.
قبل عرض الطريقة سأناقش معنى آخر للمساواة في البرمجة، ربما المعنى المألوف لكم في الرياضيات. المعنى الأول هو الإسناد، بعلامة واحدة، حيث تُربط قيمة اليمين بمتغير اليسار. هذا نعرفه.
صفحة المصدر 14
آنا بيل: علامتا المساواة في Python طريقة السؤال إن كان التعبيران متساويين — أو متكافئين، عفوًا لا متساويين. كيف نعبّر عن التكافؤ؟ إن كان شيء يساوي آخر، نقول نعم أو لا، صحيح أو خطأ. ينبغي أن يذكّركم ذلك بالنوع المنطقي (Boolean) الذي رأيناه في المحاضرة الماضية.
والآن حين نعرض المساواة أو الشروط في البرمجة، سنتحدث أكثر عن القيم المنطقية. لا يلزم أن تكون التعبيرات عددية؛ قد تعطينا نتيجة منطقية. مثل 2 < 3 صالح في Python، ويُقيَّم إلى قيمة معينة غير عددية، هي True، لأن 2 أصغر من 3.
علامتا المساواة هنا طريقة طلب معرفة تكافؤ شيئين. وهذا سيكون القيمة المنطقية False. إليكم معاملات أخرى يمكن تطبيقها على أي نوع تقريبًا في Python. معظم الوقت نستخدمها مع الأعداد، لكن يمكن أيضًا استخدامها مع السلاسل وأمثالها.
طبعًا == تتحقق من المساواة: إذا كان i مثل j، يستبدل التعبير كله بـTrue، وإلا بـFalse. ولعدم المساواة نستخدم علامة التعجب والمساواة !=: إن كان العدد — أو كائن i — لا يساوي كائن j، فالتعبير كله صحيح، وإن تساويا فهو خاطئ. ولدينا أيضًا أصغر من، وأصغر من أو يساوي، وأكبر من، وأكبر من أو يساوي.
يمكن تطبيقها على السلاسل، لكن انتبهوا لحالة الأحرف. مثلًا تكافؤ a الصغيرة وA الكبيرة خاطئ لأنهما محرفان مختلفان.
حين نتحدث عن المعاملات المنطقية، نستطيع دمجها. مثل التعبير 2 < 3 المكتوب على اللوح، صحيح. يمكن دمجه بآخر. بل وحده يمكن السؤال عن not (2 < 3)، وهو خاطئ، عكسه.
يمكن أيضًا جمع تعبيرات منطقية: ما 2 < 3 and 3 < 4؟ الأول صحيح والثاني صحيح. جمعهما هو صحيح و«و» صحيح، أي صحيح. إذا كان أحدهما صحيحًا والآخر صحيحًا، فكلاهما — وكلاهما معًا — صحيح.
إذا كان أحدهما خاطئًا، مثل 3 > 4، فما خاطئ و«و» صحيح؟ خاطئ. إذا كان أحد الطرفين خاطئًا، فالتعبير كله خاطئ. لا تحتاجون حفظ جدول الحقيقة (Truth Table)؛ يمكن التحقق كما فعلت في وحدة التحكم.
لكن إجمالًا، مع and بين تعبيرين منطقيين نحتاج كليهما صحيحًا ليكون الناتج صحيحًا. والآخر الذي نستخدمه عادة or. هو صحيح دائمًا إلا إذا كان الطرفان خاطئين. وهذا منطقي بالإنجليزية أيضًا: إن كان أي طرف صحيحًا، فالنتيجة كلها صحيحة؛ وإن كان كلاهما خاطئًا، فـor بينهما خاطئ أيضًا.
صفحة المصدر 15
آنا بيل: مثال صغير لاستخدام المعاملات. يمكن رسم مخطط الذاكرة أيضًا: pset_time هي 15، هذا متغيري؛ وsleep_time هي 8، هذا الآخر. أطبع sleep_time > pset_time. تأخذ عبارة الطباعة التعبير الذي يُقيَّم إلى False. 8 أقل من 15 خاطئ، فيطبع False.
ثم متغيران آخران، وهما منطقيان: derive صحيح وdrink خاطئ، فتكون drink and derive خاطئة لأن أحدهما خاطئ. هنا متغير آخر both، ثم أطبع False في وحدة التحكم.
تمرين سريع لكم: اكتبوا برنامجًا يحفظ عددًا سريًا في متغير. سيكون سر البرنامج. يفترض أن مستخدميه لا يقرؤون الشيفرة بل يتفاعلون معه في الصدفة فقط. احفظوا عددًا سريًا، واسألوا المستخدم أن يخمّن عددًا، ثم اطبعوا صحيحًا أو خاطئًا بحسب مطابقته للسر.
التمرين هنا. يمكن البدء بـsecret = ثم عددكم المفضل، 5 مثلًا، ثم البقية. اطلبوا التخمين واطبعوا قيمة منطقية بحسب مساواته للسر. سأعطيكم دقيقتين. نعم؟ عفوًا.
أحد الحضور: هل نضع رمز «و» مثل [غير مسموع]؟
آنا بيل: الرمز «و» ليس الشيء نفسه. يجب كتابة A-N-D في Python. رمز «و» يعني شيئًا آخر؛ معامل على بتّات العدد، فلا يعطي الإجابات نفسها. نعم، تفكر في Java أو C++ أو نحوها، صحيح؟ نعم. هل لدى أحد بداية؟ كيف آخذ الإدخال؟ guess = input؟
أحد الحضور: [غير مسموع]
آنا بيل: نعم، يمكن أن نكون لطفاء: «رجاءً خمّن —».
أحد الحضور: [غير مسموع]
آنا بيل: ماذا؟
أحد الحضور: [غير مسموع]
آنا بيل: نريد أن يعطينا عددًا صحيحًا، نعم، عددًا، بالضبط. إن تركناه هكذا نأخذ سلسلة فقط، فيجب التحويل إلى عدد صحيح. والآن؟ كيف أفحص التكافؤ بين السر والتخمين؟
أحد الحضور: [غير مسموع]
آنا بيل: secret == guess، وتريد طباعة ذلك، نعم. لنطبعه. نشغّل ونخمّن شيئًا مختلفًا: False. نعيد التشغيل ونخمّن الشيء نفسه: True. ويمكن تخمين عدد أقل لنتأكد — هل الجميع — نعم؟
أحد الحضور: هل تُسنِدين — مثلًا لمختلف [غير مسموع]، هل أسندتِ [غير مسموع] يساوي guess إلى حاوية؟
ملاحظة مترجم: في الكلام «8 أقل من 15 خاطئ» زلة مقارنة محفوظة؛ التعبير الذي يُشرح هو
8 > 15، ونتيجتهFalse.
صفحة المصدر 16
آنا بيل: نعم، بالضبط. equal تساوي هذا الشيء، نعم، ثم تفعل بها ما تريد. تطبع equal مثلًا. هذا نفسه، لكن يمكن فعل أشياء أخرى بالمتغير. نعم، بالضبط، 5، نعم.
في البيت، جرّبوا ما يحدث إن لم تحوّلوا الإدخال لعدد صحيح: هل ينهار البرنامج، أم يعمل لكنه يعطي إجابة خاطئة؟ لماذا نستخدم القيم المنطقية؟ إنها متغيرات مهمة لأنها تتيح التفكير في برامج تتخذ قرارات. نقول مثلًا: إن كان هذا صحيحًا، افعل هذا، وإلا افعل ذاك.
الجزء المنطقي هو «إن كان شيء ما صحيحًا». هذا الشيء هو القيمة المنطقية التي ننشئها في برامجنا، و«افعل هذا» أمر، و«وإلا افعل ذاك» مجموعة أوامر أخرى.
تعبير منطقي بسيط جدًا: إنها منتصف الليل، تتلقّى بريدًا عن طعام مجاني، هل تذهب إليه أم تنام؟ أبسط نقطة قرار. لكن بالشروط تستطيع كتابة برنامج رائع يأخذك للطعام المجاني.
لنفترض هذه خريطة MIT، هنا أنت وهناك الطعام. نستطيع كتابة خوارزمية بسيطة بالشروط فقط توصلك إليه. الخوارزمية هكذا: أمشي دائمًا في هذا الاتجاه، إلى الأمام أو الخلف أو اليسار أو اليمين دون دوران، وأجعل يدي اليمنى دائمًا على جدار.
إن كان يميني خاليًا — وأنا هنا يميني خالٍ — أواصل السباحة حتى أصل لجدار. إن كان اليمين مسدودًا والأمام خاليًا، أواصل هكذا إلى نهاية الغرفة. إن كان اليمين والأمام مسدودين كما عند نهاية الغرفة، أذهب يسارًا. وإن كان اليمين والأمام واليسار مسدودين، كما هناك، أذهب للخلف، للخلف.
إذن بدأت هنا ومررت حول الغرفة كلها، وأخرج من الباب إلى الممر. إذا لم تكن في خريطة MIT جزر، أي لم يكن الطعام على جزيرة في الوسط، بل متاهة عادية، فسأصل إليه في النهاية باتباع هذه الخوارزمية الشرطية البسيطة جدًا.
كيف ننفّذ الشروط في Python؟ كيف نقول: أريد إنشاء — إدراج نقطة قرار هنا؟ بالكلمة المفتاحية if، التي تبدأ كتلة قرار. أبسط كتلة هي if وحدها. يتبع Python شيفرة، ويصل في لحظة ما إلى if، فيطلب الشرط أن يفحص هل هو صحيح.
إن كان صحيحًا — هذه نقطة قرارنا — أنحرف عن البرنامج الرئيسي وأنفّذ الشيفرة التابعة له، هذين السطرين الممثّلين بالنقاط. وإن لم يكن الشرط خاطئًا، فلن أسلك ذلك الطريق، بل أواصل البرنامج الرئيسي.
كيف يعرف Python كم سطرًا ينفّذ ضمن الشرط؟ ينظر للإزاحة البادئة (Indentation). وضعت مسافات أمام الكتلتين الممثلتين بالنقاط. كل ما أُزيح مباشرة بعد if ونقطتيها هو أوامر هذه الكتلة، تُنفّذ كلها معًا. هذه if بسيطة: إمّا الأوامر الإضافية إن صح الشرط، أو لا.
ملاحظة مترجم: ورد «إن لم يكن الشرط خاطئًا» في التفريغ، وحُفظت الزلة؛ المقصود في وصف المسار الآخر أن الشرط غير صحيح.
صفحة المصدر 17
آنا بيل: يمكن إضافة استثناء. نتبع البرنامج ونصل لشرط if هنا. إن صح، ننحرف وننفّذ هذه الأوامر. وإلا فالشرط غير صحيح وننفّذ الأوامر الأخرى هنا، هذه.
إمّا هذه المجموعة أو الأخرى؛ لا ننفّذهما معًا أبدًا ولا نتجاوزهما معًا. بعد انتهاء الكتلة المزاحة، التابعة للشرط أو الأخرى عند عدم صحته، نعود لبقية البرنامج ونواصل التنفيذ. بقية البرنامج عند مستوى الإزاحة نفسه لـif وelse الأصلية.
يمكن إضافة شروط كثيرة، لا مجرد إن صح فافعل هذا وإلا ذاك. نضيف فحوصًا بـelif، اختصار «وإلا، إذا صح شرط آخر، افعل هذا». نصل لنقطة القرار: إن صح الشرط ننفّذ الأوامر كالسابق؛ وإلا نفحص آخر.
وإلا إن صح هذا الآخر، ننفّذ المجموعة الأخرى، وإلا هناك elif أخرى نفحص شرطها، إن صح ننفّذ أوامر أخرى، وإلا قد تكون هناك المزيد. وفي لحظة ما نعود لبقية البرنامج.
تُفحَص هذه الشروط واحدًا واحدًا. أول صحيح هو الذي يُنفَّذ. لا ننفّذ أكثر من واحد، لأنها «إذا، وإلا إذا، وإلا إذا». حتى بالإنجليزية تنفّذون واحدة لا جميعها. لكن يمكن تجاوز الجميع إن لم يصح أي شرط؛ لا نفعل أيًا منها. وإن صح أكثر من واحد، نفعل أول صحيح.
إن أردتم حالة شاملة، نسخة من if وelif وelif في الوسط، أضيفوا else في النهاية: افعل هذا إن لم يصح شيء. مثل ما كان هنا، إن صح هذا، وإلا ذاك. إن صح أيّ من هذه الشروط افعل واحدًا منها، وإلا هذا.
مثال: pset_time نضع بعض المتغيرات، وsleep_time كذلك، ونشغّل لنرى. لدي كتلة if وelif وelse. الشرط الأول يفحص هل مجموعهما أكبر من 24 ويفعل شيئًا. هذه كتلته، لاحظوا الإزاحة المعتادة أربع مسافات.
elif: إن لم يصح الأول أفحص التالي، هل المجموع أكبر من أو يساوي 24؟ ثم ننفّذ الطباعة هنا. وإن لم يصح أي منهما، أنفّذ هذه الكتلة، هذين السطرين.
أسميها حالة شاملة لأن الشروط الأخرى لم تصح، فنلتقط ما بقي وننفّذ الأوامر هنا.
وعندما ننتهي من هذه أو هذه أو التقاط ما تبقى، نقيّم عبارة الطباعة هنا ونطبع "end of day"، لأنها بقية برنامجي. لاحظوا مستوى إزاحتها مثل البرنامج الأصلي.
صفحة المصدر 18
آنا بيل: هذا — أوه — البرنامج. إن كانت pset_time وsleep_time هما 22 و8، فالمجموع أكثر من 24، فيدخل الكتلة ويطبع impossible. وإن كان 24 بالضبط، أي 22 و2، لا ندخل الأولى بل هذه لأنه مساوي وليس أكبر؛ نطبع full schedule، ثم نعود ونطبع "end of day".
وإلا، إن كان أقل من 24 وليس مساويًا، فلا يصح أي شرط، وندخل else وننفّذ السطرين. يأخذان القيمة المطلقة (Absolute Value) لـ24 - pset_time - sleep_time ليحسبا الوقت المتبقي في اليوم، ويطبعان هذا السطر، ثم يعودان لبقية البرنامج لطباعة "end of day".
فحص سريع: لا كتابة ولا تشغيل. فكروا في البرنامج. ما مشكلته؟ آخذ عددًا لـx وآخر لـy، وأفحص هل هما متماثلان وأطبع ذلك. إن أعطيته 5 و5 أطبع «5 مثل 5»، وأريد أيضًا طباعة «هذان متساويان». ما المشكلة؟ نعم؟
أحد الحضور: إذا لم تكن x تساوي y سيطبع أيضًا «هذان متساويان»، لأنه ليس [غير مسموع].
آنا بيل: بالضبط، إذا اختلفا نعود لبقية البرنامج لأن إزاحة هذه الطباعة كإزاحة بقيته. كيف نصلح؟ نزيحها، نعم، نزيحها إلى الداخل لتكون عند مستوى عبارة if نفسه.
يمكن في الواقع تداخل عبارات الإزاحة — تداخل الشروط (Nested Conditionals)، فبعد إنشاء شرط يكون مجرد كتلة شيفرة. هنا if بكتلتها، وداخلها يمكن وضع المزيد من if لا تُنفَّذ إلا إن صح هذا الشرط. هذه الكتلة الداخلية.
مثلًا ننفّذ الداخلية إذا تكافأت x وy، فندخل هذه الكتلة لأنها صحيحة ونطبع تساويهما. ثم الشرط الثاني y != 0 صحيح أيضًا، فنطبع هذا أيضًا.
نفّذت أحد الشروط؛ صحيح، فأتجاوز elif وelse وأعود لبقية البرنامج. الحالات الأخرى، حين تختلف القيمتان، تأخذني إما لـelse ثم لبقية البرنامج، أو حين تتكافآن أذهب — أو هنا ليس لدي فعلًا مثال لهذه الحالة في الشرائح.
لكن حين تتكافآن وy تساوي 0، لا أدخل الشرط الداخلي: مع أن x وy صحيحتان — متكافئتان، وهذا صحيح — فإن y صفر، فعدم مساواتها للصفر خاطئ. إنه معكوس فقط. ثم نعود لبقية البرنامج. نعم؟
أحد الحضور: ماذا فعلتِ بـfloat [غير مسموع]؟
آنا بيل: أحوّل الأعداد إلى float؛ يمكن إلى int أيضًا. نعم، كي لا أقارن سلاسل. الآن بعدما قدّمت الشروط، من المهم التدرب أكثر لتكوين نموذج ذهني لتتبّع الشيفرة، وبنيتها البصرية تساعد كثيرًا.
صفحة المصدر 19
آنا بيل: Python فريدة بهذا المعنى. لا توجد لغات أخرى تجبركم فعلًا على الإزاحة. لا تجبركم على مطابقة هذه البنية البصرية لما يحدث تمامًا. لكنها مفيدة جدًا في Python، وهذا ما أحبه: تساعد على رؤية الأشياء فورًا، مثل أن هذه الشيفرة جزء من هذه الكتلة، فتساعد على التنقيح بكفاءة أكثر.
كلما تدربتم اعتدتم تتبع الشيفرة، ومعرفة أين تذهب إذا كانت المتغيرات بهذه القيم. سأتجاوز هذا التمرين لأنه تتبّع فقط، وأطلب هذا — أو نكتبه سريعًا، أو تبدأون ثم نكتبه معًا. هو تنويع للبرنامج الذي كتبتم.
بدل إخباري هل التخمين صحيح — أو مطابق للسر — اطبعوا هل هو أصغر من المطلوب، أو أكبر، أو مطابق. نحتاج شرطًا: إن صح شرط نطبع شيئًا. أعطيكم دقيقة ثم نكتب معًا وننتهي. نعم؟
أحد الحضور: هل يمكن عبارتا if في البرنامج؟
آنا بيل: نعم. توجد تمارين لكم في البيت فيها عبارتان لتروا ما يحدث. هذا يبدأ شرطين. يمكن أن يصح شرط، ويصح الآخر أيضًا، فيُنفَّذ كلاهما. ليست حالة «وإلا». سؤال جيد.
سأنسخ الإدخال السابق. هل لدى أحد بداية للشرط؟ نسخت ما كان لدينا للإدخال. نعم؟
أحد الحضور: إذا كانت x [غير مسموع] ويساوي [غير مسموع]؟
آنا بيل: نعم، نعم.
أحد الحضور: ثم نطبع —
آنا بيل: نطبع.
أحد الحضور: تخمينك «أكبر من المطلوب».
آنا بيل: نعم، جيد، بداية رائعة. يمكن التشغيل وتخمين شيء نعرف أنه أكبر. ممتاز، أكبر، نعم. التالي؟ أتريدون else أم elif؟ نعم؟
أحد الحضور: في الواقع أحذف المساواة، لأننا إن أدخلنا 5 سيقول أيضًا أكبر.
آنا بيل: نقطة جيدة. لنشغّل بـ5، يقول أكبر، بالضبط. نحذف المساواة؛ جيد أننا نقّحناه. نضع elif للتخمين المكافئ للسر ونطبع equal. أوه.
هل يفهم الجميع حذف المساواة من علامة أكبر من؟ لأننا كنا سنفوت — كنا سندخل المسار الأول خطأ. لكن بـelif نضع حالة التخمين المكافئ للسر ونطبع equal.
صفحة المصدر 20
آنا بيل: والأخيرة يمكن أن تكون else، لأن الخيار الوحيد الآخر هو تخمين أصغر، أو elif أخرى إن أردنا. نتركها else ونطبع "too low". ثم نشغّل ونجرب جميع الحالات: أكبر، ومطابق، و— لا أدري ماذا فعلت هناك، ينبغي إعادة تشغيل النواة. جربنا الأكبر والمكافئ، ثم نجرب الأصغر. نعم؟
أحد الحضور: ما الفرق بين else وelse-if [غير مسموع]؟
آنا بيل: لا فرق. يمكن كتابة elif guess < secret، فيعمل تمامًا كما هو. else أسرع لأننا نعرف عدم وجود خيارات أخرى.
أحد الحضور: لماذا لا نضع صفًا من عبارات if؟
آنا بيل: يمكن في هذه الحالة أيضًا، لكن يجب الحذر أن تكون متنافية (Mutually Exclusive). كما في المثال السابق، إذا كانت شروط كثيرة قد تصح جميعًا، تُنفَّذ عبارات if كلها. لأن if تبدأ كتلة، وelif مرتبطة بها: إما هذه أو الأخرى أو الأخرى. لكن مجموعة if قد تصح كلها وتُنفَّذ كلها. نعم، نعم؟
أحد الحضور: لماذا لا نستخدم أقواسًا مع else؟
آنا بيل: يمكن استخدامها في عبارات if-else. تقصد هكذا؟ نعم، خصوصًا مع تعبيرات كثيرة معًا، لكن إذا كان واحدًا فقط يعرف Python تلقائيًا أن يقيّمه أولًا ثم ينفّذ if. هذه كلها أسئلة رائعة بالمناسبة. حسنًا.
كما رأينا، كان هناك عيب صغير في الشيفرة، وجيد أننا شغّلناها. كان عليّ تشغيلها بخيارات مختلفة كثيرة. من المهم التنقيح مبكرًا ومتكررًا لئلا تدخلوا عيبًا يستمر عبر الشيفرة. هذه فكرة كبرى أخرى.
وملخص سريع لما تعلمناه: الإدخال والإخراج يجعلان برامجنا تفاعلية طبعًا. أضفنا التفرّع لإدراج نقاط قرار. في المرة المقبلة مزيد منه قليلًا ثم الحلقات، طرق تكرار الأوامر. تجاوزت الوقت قليلًا؛ لن أفعل مجددًا.