[MUSIC PLAYING]
ديفيد ج. مالان: حسنًا، هذه هي دورة CS50 وهذه هي المحاضرة الرابعة.
إذن نحن هنا في قاعة محاضرات لويل الجميلة
وسنستخدم Sanders اليوم.
وانضم إلينا بعض الأصدقاء الذين
سيأتون ويظهرون في غضون لحظات.
ولكن قبل ذلك، تذكّروا آخر مرة ألقينا فيها نظرة على CS50 IDE.
كانت تلك عبارة عن بيئة برمجية جديدة قائمة على الويب تشبه في جوهرها
CS50 Sandbox وCS50 Lab، ولكن أضيفت إليها بعض الميزات.
على سبيل المثال، ما هي الميزات التي أضافتها إليك--
لقدراتك؟
نعم؟
الجمهور: مصحح الأخطاء.
ديفيد ج. مالان: ماذا؟
الجمهور: مصحح الأخطاء.
ديفيد ج. مالان: مصحح الأخطاء.
إذن debug50، الذي يفتح تلك اللوحة الجانبية التي
تتيح لك المضي خلال تعليماتك البرمجية، خطوة بخطوة، ورؤية المتغيرات.
نعم؟
الجمهور: Check50.
ديفيد ج. مالان: معذرة، قل ذلك مجددًا؟
الجمهور: Check50.
ديفيد ج. مالان: Check50 كذلك، وهي أداة محددة من CS50
تتيح لك التحقق من صحة تعليماتك البرمجية
تشبه إلى حد كبير ما سيقوم به زملاء التدريس أثناء تقديم الملاحظات بشأنها.
تشغيل سلسلة من الاختبارات التي تشبه إلى حد كبير
الاختبارات نفسها التي سيشجعكم الكثير من الواجبات المنزلية
على تشغيلها يدويًا،
لكنها تعمل فقط على تشغيل العملية تلقائيًا.
وأي شيء آخر؟
الجمهور: [INAUDIBLE]
ديفيد ج. مالان: إذن هذا صحيح أيضًا.
توجد وظيفة مخفية إلى حد ما والتي لا نستخدمها في هذا الفصل الدراسي،
لكن نعم بالفعل.
إذا كنت تبحث عن قطعة لغز صغيرة، فيمكنك
بالفعل تحويل تعليمة C البرمجية الخاصة بك إلى سكراتش مثل قطع اللغز
ذهابًا وإيابًا، وذهابًا وإيابًا، بفضل كريم وبعض أفراد الفريق.
لذلك هذا هناك، ولكن الآن، ربما من الأفضل
التعود على النص أيضًا.
إذن يوجد لدينا بعض الأدوات الأخرى التي قمنا
باستخدامها طوال وقت الدورة بجانب check50 وdebug50.
لقد استخدمنا بالطبع printf ومتى يكون printf مفيدًا؟
على سبيل المثال عندما ترغب في استخدامه بعدما تجاوزنا مرحلة طباعة شيء ما فقط
لأن مجموعة المشاكل تخبرك بذلك.
أجل؟
الجمهور: للعثور على مكان وجود الخطأ.
ديفيد ج. مالان: نعم، إذن للعثور على مكان وجود الخطأ.
إذا كنت فقط ترغب نوعًا ما في طباعة متغيرات، أو قيمة أو نص
إذن تعرف ما يحدث ولا تريد بالضرورة
نشر debug50، يمكنك القيام بذلك.
متى أيضًا؟
الجمهور: إذا كانت لديك صيغة طويلة لشيء ما [INAUDIBLE]
وتريد أن ترى [INAUDIBLE].
ديفيد مالان: جيد.
نعم.
الجمهور: كيفية التشغيل-- مثل التنقل من خلال debug50 خمسين مرة.
ديفيد ج. مالان: بالفعل.
حسنًا، في الحياة الواقعية-- إذن قد ترغب في استخدام printf
عندما يكون لديك تكرار حلقي متداخل، وترغب في وضع printf داخل التكرار الحلقي
حتى ترى متى يبدأ المفعول.
بالطبع، يمكنك استخدام debug50، ولكن
قد ينتهي الأمر بتشغيلك debug50 أو النقر فوق التالي، التالي، التالي، التالي، التالي، التالي،
التالي لمرات عديدة الأمر يصبح مملاً بعض الشيء.
ولكن ضَع في اعتبارك، أنه يمكنك وضع نقطة توقف أعمق في تعليماتك البرمجية
أيضًا ومن المحتمل إزالة نقطة توقف لوقت سابق كذلك.
وبصراحة، طوال الوقت، سواء كان في لغة C أو لغات أخرى،
هل أجد نفسي أحيانًا أستخدم printf فقط لكتابة printf هنا
فقط حتى يمكنني حرفيًا رؤية ما إذا وصلت تعليمتي البرمجية إلى نقطة معينة هنا
لرؤية ما إذا تمت طباعة شيء ما.
لكن مصحح الأخطاء الذي ستعثر عليه من الآن
فصاعدًا أكثر قدرةً، ومتنوع الاستعمالات للغاية.
إذن إذا لم تعتد بالفعل على استخدام debug50 بكل
الطرق فابدأ باستخدم نقاط التوقف تلك للتنقل فعليًا خلال تعليمتك البرمجية
حيث تهتم برؤية ما يجري.
إذن style50، بالطبع، يتحقق من أسلوب تعليمتك البرمجية مثلما قد يقوم
بذلك زملاء التدريس، ويظهر لك باللون الأحمر أو الأخضر
ما هي المساحات التي قد ترغب في حذفها، ما هي المساحات التي قد
ترغب في إضافتها فقط لتزيين الأمور.
حتى تكون أكثر قابلية للقراءة لك وللآخرين.
ثم ماذا عن help50؟
متى يجب عليك الوصول بديهيًا إلى help50؟
الجمهور: عندما لا تفهم رسالة الخطأ.
ديفيد ج. مالان: بالضبط.
نعم، عندما لا تفهم رسالة الخطأ.
إذن أنت تقوم بتحويل شيء ما برمجيًا.
أنت تشغل أمرًا.
إنه لا يعمل تمامًا بالفعل وتشاهد رسالة خطأ مشفرة.
في النهاية، ستحصل على الذاكرة العضلية ونوع التعرض
لمجرد المعرفة، أوه، تذكرتُ ماذا يعني ذلك.
ولكن حتى ذلك الحين، قم بتشغيل help50 في بداية ذلك الأمر نفسه،
وستقوم بمحاولة كشف خطئك
وتوفير ملاحظات من زميل تدريس على كيفية العمل بالفعل حول هذا الأمر.
سترى اثنتين على موقع الدورة التدريبية على الويب عبارة عن نشرة رائعة معدّة
من قِبل إميلي هونغ، واحدة من زملائنا في التدريس،
التي تقدِّم فيها جميع هذه الأدوات، وبضع أدوات أخرى،
وتجعلك تتعود على التفكير في الأمور.
إنه مخطط انسيابي نوعًا ما.
إذا كانت لديّ هذه المشكلة، سأقوم بذلك أو أي شيء آخر
إذا كانت لديّ هذه المشكلة سأقوم بالشيء الآخر.
للتأكد من ذلك أيضًا.
لكن اليوم، دعونا نقدم الأداة الأخيرة بالفعل، وبالتأكيد للغة C،
من أدوات سطر الأوامر الخاصة بنا التي ستساعدك
في تعقب المشاكل في تعليمتك البرمجية.
في الأسبوع الماضي، تذكّروا أننا تحدثنا عن الذاكرة كثيرًا.
تحدثنا عن malloc، وعن تخصيص الذاكرة،
وتحدثنا عن تحرير مساحة الذاكرة وما شابه ذلك.
ولكن تبين، أنه يمكنك خلق الكثير من الضرر
عندما تبدأ بالعبث في الذاكرة.
في الحقيقة، ربما الآن، تقريبًا الجميع-- خطأ التجزئة؟
[LAUGHTER]
نعم، إذن هذا مجرد خطأ واحد من الأخطاء التي ربما تواجهها،
وبصراحة، قد يكون لديك أخطاء في تعليمتك البرمجية من الآن
فصاعدًا وهي تحتوي على أخطاء ولكنك لا تدرك وجودها حتى
لأنك محظوظ فقط.
ولن يتعطل البرنامج أو يتوقف تمامًا،
لكن يمكن أن يحدث هذا.
ولذا Valgrind هو برنامج سطر أوامر ومن المحتمل
أن يبدو على أنه الأداة الأكثر رعبًا من بين الأدوات التي استخدمناها،
ولكن يمكنك أيضًا استخدامها مع help50، وهي
تحاول فقط العثور على ما يسمى بتسريبات الذاكرة في برنامجك.
تذكّروا أننا في الأسبوع الماضي قدّمنا ​​malloc،
ويتيح لك malloc تخصيص الذاكرة.
ولكن إذا لم تحرر تلك الذاكرة، من خلال تسمية الدالة الفارغة حرفيًا،
فستطلب باستمرار من نظام التشغيل لديك، MacOS، Linux،
Windows، أيًا كان، هل يمكنني الحصول على المزيد من الذاكرة؟
هل يمكنني الحصول على المزيد من الذاكرة؟
وإذا لم تسترجعها، حرفيًا، أبدًا من خلال طلب التحرير فقد يصبح جهاز الكمبيوتر
الخاص بك بطيئًا أو يتوقف عن العمل أو يتعطل.
وبصراحة، إذا سبق وحدث ذلك على جهاز Mac أو PC لديك، فمن المحتمل جدًا
أن هذا ما فعله بعض البشر عن طريق الخطأ.
يقوم هو أو هي فقط بتخصيص المزيد والمزيد من الذاكرة
ولكن لم يتم تحرير تلك الذاكرة بالفعل.
إذن Valgrind يمكنه مساعدتك في العثور على تلك الأخطاء قبل أن تقوم أنت أو المستخدمون لديك بذلك.
إذن دعونا نقوم بمثال سريع، دعوني أنتقل إلى CS50 IDE، ودعوني أمضي قدمًا
وأقوم بإعداد برنامج جديد هنا.
سنسميه memory.c لأننا سنرى لاحقًا اليوم كيف
يمكنني تعقب تسربات الذاكرة تلك.
ولكن الآن، لنبدأ بشيء مبسط أكثر، وربما قمتم بتنفيذه
جميعكم حتى الآن، وهو لمس الذاكرة عن طريق الخطأ
والتي كان يجب عليكم عدم، تغييرها، أو قراءتها ودعونا نرى ما الذي قد يعنيه هذا.
إذن دعونا نقوم بما اعتدنا فعله في الأعلى هنا.
Include standard IO "تضمين إدخال/إخراج قياسي".
حسنًا، دعونا ألا نقوم بذلك الآن.
دعونا فقط نقوم بهذا أولاً.
دعونا نقوم بكتابة int، main(void)، فقط لكي نبدأ برنامجًا بسيطًا
وهنا دعوني أمضي قدمًا وأستدعي فقط دالة باسم f.
ولا أهتم في الحقيقة بماهية اسمها اليوم.
أنا أريد فقط استدعاء دالة f، ومن ثم فهذا كل شيء.
الآن هذه الدالة f، دعوني أمضي قدمًا وأعّرفها على النحو التالي، (void f(void.
لن تفعل الكثير مطلقًا.
لكن دعونا نفترض، فقط من أجل المناقشة، هذا هو هدف f في الحياة
هو تخصيص الذاكرة فقط لأي غرض مفيد،
ولكن الآن إنها للإثبات فقط.
إذن ما هي الدالة التي يمكنك تخصيص الذاكرة باستخدامها؟
الجمهور: Malloc.
ديفيد ج. مالان: Malloc.
إذن افترضوا أنني أريد مساحة malloc من أجل، لا أدري،
شيء بسيط مثل عدد صحيح واحد فقط.
نحن نقوم بهذا فقط لأغراض التوضيح،
أو في الواقع دعونا نقوم بالمزيد، 10 أعداد صحيحة، 10 أعداد صحيحة.
يمكنني، بالطبع، القيام-- حسنًا، أعطني 10، لكن كم وحدة بايت أريدها؟
كم وحدة بايت أحتاجها لـ 10 أعداد صحيحة؟
الجمهور: sizeof(int).
ديفيد ج. مالان: أجل، إذن يمكنني كتابة sizeof(int)
وعلى الأرجح فكم سيكون حجم العدد الصحيح؟
الجمهور: أربعة.
ديفيد ج. مالان: أربعة، ربما.
في العديد من الأنظمة اليوم، يبلغ 4 وحدات بايت فقط أو 32 بايت،
ولكنك لا تريد تعليمات برمجية ثابتة حتى لا يستخدم كمبيوتر شخص آخر
هذه القيم نفسها.
إذن حجم عدد صحيح.
إذن 10 أضعاف حجم العدد الصحيح.
يعيد Malloc أي نوع من البيانات؟
ما الذي يُعيده هذا إليّ؟
الجمهور: [INAUDIBLE]
ديفيد ج. مالان: أجل، يعيد عنوانًا أو مؤشرًا.
بالتحديد، العنوان، 100، 900، أيًا كان، من جزء الذاكرة
الذي تم تخصصيه لك للتو.
إذن إذا رغبت في الاحتفاظ بهذا هنا، سأحتاج إلى إعلان مؤشر.
دعونا نسميه x اليوم والذي يخزِّن ذلك العنوان.
يمكن أن نسميه x، y، z، أيًا كان، لكنه ليس العدد الصحيح الذي تتم إعادته.
وإنما عنوان العدد الصحيح.
وتذكّروا، ما الذي يعنيه مُشغل النجمة الآن.
عنوان بعض أنواع البيانات.
إنه مجرد رقم.
حسنًا، الآن إذا كنت--
دعونا أولاً، نمحو هذا.
يتضح أنك تستخدم malloc، أحتاج إلى استخدام stdlib.h.
الذي شاهدناه الأسبوع الماضي، وإن كان لفترة وجيزة، وبعد ذلك بالطبع
إذا كنت سأستدعي f، فما الذي يتعين عليّ القيام به لإصلاح هذه التعليمة البرمجية؟
الجمهور: أنت بحاجة إلى إعلان.
ديفيد ج. مالان: اجل، أحتاج إلى إعلانه هنا،
أو يمكنني فقط تحريك تنفيذ f إلى الأعلى.
إذن أعتقد ان الأمر نجح، رغم أن هذا البرنامج في الوقت الحالي
أحمق للغاية.
إنه لا يقوم بأي شيء مفيد، ولكنه سيخصص الذاكرة.
وسأفعل شيئًا باستخدامه على النحو التالي.
إذا كنت أرغب في تغيير القيمة الأولى في جزء الذاكرة هذا،
حسنًا كيف يمكنني القيام بذلك؟
حسنًا، لقد طلبت من الكمبيوتر 10 أعداد صحيحة أو بالأحرى مساحة
لـ 10 أعداد صحيحة.
الشيء المثير للاهتمام حول malloc هو أنه عندما
يعيد جزءًا من الذاكرة إليك إنه متجاور، على التوالي.
وعندما تسمعون متجاور أو على التوالي،
فما هو نوع بنية البيانات التي تتبادر في ذهنكم؟
الجمهور: مصفوفة.
ديفيد ج. مالان: مصفوفة.
إذن يتضح أنه يمكننا التعامل مع هذا الجزء العشوائي فقط من الذاكرة
كأنه مصفوفة.
لذلك إذا أردنا الانتقال إلى الموقع الأول في تلك المصفوفة من الذاكرة،
يمكنني فقط القيام بذلك وأضع رقمًا لنقل 50.
أو إذا أردت الانتقال إلى الموقع التالي، يمكنني القيام بذلك.
أو إذا كنت أردت الانتقال إلى الموقع الأخير، فقد أقوم بذلك،
ولكن هل هذا جيد أم سيئ؟
الجمهور: سيئ.
ديفيد ج. مالان: لماذا سيئ؟
الجمهور: إنه-- خارج الحدود
ديفيد ج. مالان: أجل، إنه إذن خارج الحدود.
أليس كذلك؟
هذا هو نوع أخطاء أسلوب الأسبوع الأول عندما تعلق الأمر بالتكرارات الحلقية.
تذكرون، مع التكرار حلقي أو أثناء تكرارات حلقية، قد تذهب بعيدًا للغاية،
وهذا جيد.
ولكن الآن سنرى بالفعل أن لدينا أداة
يمكن أن تساعدنا في ملاحظة هذه الأشياء.
إذن آمل، فقط بشكل مرئي، أنه من الواضح أن الذي يحدث هنا
هو فقط-- على السطر 12، لديّ متغير x
الذي يخزِّن العنوان لهذا الجزء من الذاكرة.
ثم على السطر 13، أحاول فقط الوصول إلى موقع 10
وأقوم بتعيين القيمة 50 هناك.
ولكن كما تلاحظون، لا يوجد موقع 10.
توجد مواقع 0، 1، 2، 3، بنفس النسق خلال 9، بالطبع.
إذن كيف يمكننا اكتشاف هذا باستخدام برنامج؟
حسنًا، دعوني أمضي قدمًا وأوسع النافذة الطرفية قليلاً
هنا، وأحفظ ملفي، ودعوني أمضي قدمًا وأحول make memory برمجيًا.
حسنًا، كل شيء على ما يرام.
تم تحويلها برمجيًا بدون أي رسائل أخطاء، والآن
دعوني أمضي قدمًا وأشغل الذاكرة، enter.
حسنًا، إذن هذا يعمل بشكل جيد للغاية.
دعونا في الواقع نكون أكثر وضوحًا هنا فقط لتدبر الأمور جيدًا.
دعوني أمضي قدمًا وأطبع شيئًا ما.
إذن printf، %i لعدد صحيح، ودعونا نجعله أكثر وضوحًا.
قمتَ بإدخال %i ومن ثم فاصلة x قوس 10.
وما الذي يتعين عليّ القيام به لتضمينك باستخدام printf؟
الجمهور: stdio.h.
ديفيد ج. مالان: نعم، إذن stdio.
إذن دعونا فقط بسرعة نضيف هذا، stdio.h، حفظ.
حسناً، دعوني أعيد تحويل هذا برمجيًا، make memory، enter.
والآن دعوني أمضي قدمًا وأكتب ./memory.
ها؟
يبدو أنه البرنامج الصحيح.
ومع ذلك، لبضعة أسابيع الآن كنا نزعم أن امم-مم،
لا تفعل ذلك.
ولا تتجاوز حدود مصفوفتك.
إذن كيف يمكننا تسوية هذا الأمر؟
يبدو وكأنها تعليمة برمجية خاطئة أو على الأقل لقد أخبرناكم أنها تعليمة برمجية خاطئة،
ومع ذلك نجح الأمر.
أجل؟
الجمهور: [INAUDIBLE]
ديفيد ج. مالان: هذه وسيلة جيدة لوضعها.
الجمهور: ما زالت متشابهة للغاية.
نحن نريد ذلك.
ديفيد ج. مالان: حسنًا.
الجمهور: إذن يمكننا من الناحية النظرية--
لقد أنشأت برنامجًا للتو.
ديفيد ج. مالان: نعم، وأعتقد إذا سمعتك بشكل صحيح،
أنك قلتِ أن لغة C لا تقوم بالتنبيه إذا ذهبت بعيدًا أكثر مما ينبغي؟
الجمهور: نعم.
ديفيد ج. مالان: أجل، حسنًا.
هذه طريقة جيدة لوضعها.
على سبيل المثال، يمكنك أن تكون محظوظًا في لغة C. ويمكنك
القيام بشيء ما خطأ من الناحية الموضوعية، التعليمية، وتقريبًا الفنية،
لكن الكمبيوتر لن يتعطل.
لن يتوقف فقط لأنك محظوظ.
لأنه في كثير من الأحيان، لأسباب تتعلق بالأداء، عندما
تخصص مساحة لـ 10 أعداد الصحيحة، فستقوم
في الواقع باسترجاع جزء من الذاكرة مجددًا
وهو أكبر مما تحتاج إليه قليلاً.
ليس من الآمن فقط أن تفترض أنه أكبر مما تحتاج إليه،
ولكن قد يحالفك الحظ فقط.
وقد ينتهي الأمر بوجود المزيد من الذاكرة لديك والتي يمكنك الحصول عليها من الناحية الفنية
باللمس أو الوصول أو التغيير، ولن يلاحظ الكمبيوتر ذلك.
ولكن هذا ليس آمنًا لأنه على جهاز Mac أو كمبيوتر شخص آخر،
قد يكون جهاز الكمبيوتر الخاص بهم يعمل فقط بشكل مختلف قليلاً عن جهازك،
وفجأة، سيعبث هذا الخطأ بها وليس بجهازك.
وهذه هي الأخطاء الأصعب والأكثر إزعاجًا لتعقبها كما قد
تعرّض البعض منكم إلى ذلك.
أليس كذلك؟
وهي تعمل على جهاز الكمبيوتر الخاص بك وليس عند الأصدقاء أو العكس بالعكس.
هذه هي أنواع التفسيرات لذلك.
إذن يمكن أن يساعدنا Valgrind في تعقب هذه الأخطاء الأكثر دقة.
يبدو أن البرنامج يعمل.
Check50 أو الأدوات المشابهة لها قد تفترض
أنه يعمل لأنها تطبع الشيء الصحيح،
ولكن دعونا نلقي نظرة على ما يفكر فيه هذا البرنامج Valgrind.
دعوني أزيد حجم النافذة الطرفية هنا،
وأمضي قدمًا وأكتب في Valgrind ./memory.
إذن اسم البرنامج نفسه ./memory لكنني بدأته بوضع اسم Valgrind.
حسنًا؟
لسوء الحظ، Valgrind قبيح تمامًا بالفعل،
ويطبع مجموعة كاملة من الأشياء هنا.
إذن دعونا نلقي نظرة.
في الأعلى، سترون جميع هذه الأرقام على اليسار،
وهذه مجرد صورة جمالية مؤسفة.
لكننا نرى بعض المعلومات المفيدة.
قراءة غير صالحة للحجم 4 ثم إنها تحتوي على هذه
الأرقام والأحرف التي تبدو مشفرة.
ما هي هذه؟
إنها مجرد عناوين والسداسيّ العشريّ.
لا يهم حقًا ما هي، ولكن Valgrind
يمكنه أن يخبرنا أين هي الذاكرة التي تتصرف على نحو مريب.
يمكنكم بعد ذلك رؤية ما بجانب ذلك، حيث يشير Valgrind إلى
دالة f على السطر 15 من memory. c.
لذا قد يكون ذلك مفيدًا، ومن ثم main على السطر 8
لأن هذه هي الدالة التي تم استدعاؤها.
إذن Valgrind في الواقع جيد إلى حد ما كونه يعرض لنا جميع الدوال
التي قمت باستدعائها من الأسفل، والتي تشبه المكدس من الأسبوع الماضي كثيرًا.
وهكذا فإن شيئًا ما يحدث على نحو خاطىء في السطر 15، وإذا عدنا إلى ذلك،
دعونا نرى أن السطر 15 كان--
حسنًا، مؤكد بما فيه الكفاية.
أنا أحاول في الواقع الوصول إلى موقع الذاكرة ذلك
وبصراحة قمت بذلك على السطر 14 أيضًا.
لذا آمل أن إصلاح أحدهما أو كليهما سيعالج تلك المشكلة.
ولاحظوا هنا، فهذا صراحةً يصبح الآن غامرًا نوعًا ما بسرعة.
ثم، أوه، فُقِدت بالتأكيد 40 وحدة بايت في كتلة واحدة في السجل المفقود.
أعني، هذه هي المشكلة في Valgrind، بصراحة.
فقد تمت كتابته منذ بضع سنوات، وليس مناسبًا للمستخدمين على وجه الخصوص،
ولكن من الجيد أن لدينا أداة لمعالجة هذا.
دعوني أمضي قدمًا وأعيد تشغيل Valgrind باستخدام help50،
enter، ونرى إذا لم نتمكن من مجرد المساعدة في هذا.
حسنًا، إذن ما تزال الكمية نفسها من المُدخل الأبيض والأسود ولكن في الأسفل هنا الآن
يقوم help50 بالملاحظة، أوه، يمكنني مساعدتك بشأن الكتابة غير الصحيحة للحجم 4.
إذن ما تزال في الموقع نفسه، ولكن هذه المرة--
أو بالأحرى الملف نفسه، memory.c ولكن في السطر 14.
ونقترح، يبدو أنك تحاول تعديل 4 وحدات بايت من ذاكرة
لا تخصك، علامة استفهام.
هل جربتم تخزين شيء يفوق مساحة المصفوفة؟
ألقوا نظرة عن قرب على السطر 14 من memory.c.
إذن آمل أن، على الرغم من أن مخرج Valgrind سري خيالي،
فسيشير على الأقل المخرج الأصفر لك نحو، آه، السطر 14.
أقوم بالفعل بلمس 4 وحدات بايت، عدد صحيح، ولا يجب فعل هذا.
إذن دعونا نمضي قدمًا ونصلح هذا.
إذا انتقلت إلى برنامجي، ولن أقوم بذلك.
دعونا نغيره إلى الموقع 9، والموقع 9 هنا وحفظ.
ثم دعوني أمضي قدمًا وأعيد تشغيل Valgrind دون استخدام help50.
حسنًا، يحدث التقدم باستثناء--
عذرًا.
كلا، لا يوجد تقدم.
لقد تخطيتُ الخطوة.
نعم، لم أُعِد تحويله برمجيًا.
ينتابني شيء من الحيرة لماذا رأيت الشيء نفسه.
إذن دعونا الآن نعيد تشغيل Valgrind وهنا يبدو أنه أفضل.
لذلك لا أرى رسالة الخطأ نفسها
في الأعلى كما فعلنا من قبل، ولكن لاحظوا هنا، توجد 40 وحدة بايت في كتلة واحدة.
حسنًا، لقد كانت تلك هي القاعدة السيئة في البرنامج، لكنها بالتأكيد
فُقِدت خلال فقدان السجل 1 من 1.
إذن ما زلت لا أفهم ذلك تمامًا.
ليس أمرًا صعبًا.
دعونا نمضي قدمًا ونشغل help50 ونرى ما الخطأ الثاني من الخطأين
الذي يبدو أنه هنا.
إذن يقوم بتظليل تلك السطور هنا.
بالتأكيد تم فقدان 40 وحدة بايت وكتلة واحدة، ويبدو أن برنامجك
قام بتسريب 40 وحدة بايت من الذاكرة.
هل نسيت الذاكرة المُحررة التي قمت بتخصيصها باستخدام malloc؟
ألقوا نظرة عن قرب على السطر 13 من memory.c.
إذن في هذه الحالة، السطر 13 لديه بالفعل استدعاء إلى malloc.
إذن كيف يمكن إصلاح هذه المشكلة؟
الجمهور: تحرير.
ديفيد ج. مالان: حسب help50 أم ببديهتك الخاصة؟
ما الذي يتعين عليّ إضافته إلى هذا البرنامج؟
الجمهور: تحرير.
نعم، تحرير، وإلى أين يؤدي ذلك؟
هنا بالضبط.
إذن يمكننا تحرير الذاكرة.
لماذا سيكون هذا سيئًا؟
الجمهور: [INAUDIBLE]
ديفيد ج. مالان: بالضبط.
نحن نحرر الذاكرة، كقولنا لنظام التشغيل،
لا أحتاج ذلك بعد الآن.
ومع ذلك، بعد سطرين فيما بعد سنستخدمه مرارًا وتكرارًا.
سيئ جدًا.
لم نفعل هذا الخطأ الأسبوع الماضي، ولكن يجب أن تحرروا فقط
الذاكرة عندما تصبحون، حرفيًا،
مستعدين لتحريرها وإعادتها مجددًا، والذي يجب أن يكون على الأرجح
في نهاية البرنامج.
إذن دعوني أمضي قدمًا وأعيد حفظ هذا، فتح، أعلى النافذة الطرفية،
وأعيد تحويلها برمجيًا هذه المرة، والآن، دعوني أشغل Valgrind للمرة الأخيرة
دون استخدام help50.
وما يزال الأمر أكثر إطنابًا قليلاً، ولكنه بدون أخطاء، بدون سياقات.
هذا يبدو جيدًا للغاية.
وعلاوة على ذلك، فإنه أيضا يقول بوضوح، أنه تم تحرير جميع كتل الكومة.
وتذكّروا أن الكومة، هي ذلك الجزء من الذاكرة
الذي رسمناه بشكل مرئي هنا، حيثما يأخذ malloc الذاكرة منه.
إذن، انتهينا.
إذن هذه نوعًا من هي طريقة التفكير التي
يجب وجودها عندما تتناول بها صحة تعليمتك البرمجية.
على سبيل المثال، هي طريقة من طرق تشغيل نماذج مدخلات، أو تشغيل البرنامج كما فعلتُ.
بدا كل شيء جيدًا.
إنها طريقة من طرق تشغيل أدوات مثل check50، الذي كتبناه نحن البشر.
ولكننا أيضًا عُرضة للخطأ، بالتأكيد، وقد لا نفكر في أي شيء.
ولحسن الحظ، قام البشر الأذكياء بإنشاء الأدوات، التي تبدو من الوهلة الأولى،
صعبة نوعًا ما لاستخدامها.
مثل debug50، كما هو Valgrind الآن.
ولكنها في نهاية المطاف تساعدك في جعل تعليمتك البرمجية صحيحة بنسبة 100٪
دون أن تضطر إلى أن تعاني بصريًا لمجرد التحديق في الشاشة.
ونرى هذا كثيرًا في الساعات المكتبية، بصراحة.
العديد من الطلاب، مع احترامهم، يستنتجون نوعًا ما من خلال، التحديق
في الشاشة، فقط لمحاولة فهم ما الذي يحدث بشكل خاطىء،
لكنهم لا يأخذون أي مدخل إضافي بخلاف الأحرف
على الشاشة.
لديك الكثير من الأدوات التي يمكن أن تمنحك الكثير والكثير من التلميحات على طول الطريق.
لذلك اكتسب تلك الغرائز.
أي أسئلة أخرى حول هذا؟
أجل؟
الحضور: سيدي، إذا كان لديك دالة main أخذت الوسيطات.
هل ستقوم بتشغيل Valgrind باستخدام تلك الوسيطات أيضًا؟
ديفيد ج. مالان: نعم، بالفعل.
إذن يعمل Valgrind تمامًا مثل debug50، تمامًا مثل help50.
إذا كانت لديك وسيطات سطر أوامر، فقط قم بتشغيلها كالمعتاد،
ولكن ابدأ الأمر الخاص بك باستخدام Valgrind، أو حتى باستخدام help50 Valgrind،
لمساعدة واحدة باستخدام الأخرى.
سؤال جيد.
أي أفكار أخرى؟
نعم؟
الجمهور: إلى أين تنتقل البيانات [INAUDIBLE]؟
ديفيد ج. مالان: سؤال جيد.
إذن في نهاية اليوم، فكّروا في الشيء
الموجود داخل جهاز الكمبيوتر، والذي يعتبر شيئًا ما كهذا تمامًا.
لذا من الناحية المادية، من الواضح أنه ما زال هناك.
إنها يتم التعامل معها فقط بواسطة نظام التشغيل--
Mac، نظام التشغيل، Windows، Linux، أيًا كان، يشبه تجمعًا في الذاكرة.
نحن نستمر في رسمها كشبكة بحيث تبدو نوعًا ما كهذا.
لذا مهمة أنظمة التشغيل هي فقط تعقب أي من هذه المربعات
قيد الاستخدام، وذلك بفضل malloc.
والذي تم تحريره.
وهكذا يمكنك التفكير في الأمر على أن لديها علامات
تحقق بجانبها لتقول إن، هذا قيد الاستخدام، هذا قيد الاستخدام،
هؤلاء الآخرين ليسوا قيد الاستخدام.
إذن عادوا فقط إلى ما يسمى بالقائمة الحرة في ذلك التجمع من الذاكرة.
سؤال جيد.
إذا كنت تأخذ دورة المستوى الأعلى حول أنظمة التشغيل في الواقع،
أو CS61 أو 161 في جامعة هارفارد، فستبني بنفسك هذه الأشياء
بالفعل.
وتقوم بتنفيذ أدوات على سبيل المثال، malloc، بنفسك.
أجل؟
الجمهور: إذن لماذا يجب أن نخصص ذاكرة في هذه الحالة، وما الذي يحدث
[INAUDIBLE]؟
ديفيد ج. مالان: سؤال جيد.
لماذا يجب أن نخصص ذاكرة في هذه الحالة؟
لم نفعل.
كان هذا ببساطة، كما ذكرت، لأغراض التوضيح.
إذا كانت لدينا بعض البرامج التي أردنا فيها
تخصيص قدر من الذاكرة، لذا هذه هي الكيفية التي قد نقوم بها.
ومع ذلك، أفضل طريقة للقيام بكل هذا،
كانت بقول، مرحبًا، جهاز الكمبيوتر، أعطني 10 أعداد صحيحة هكذا،
ولا داعي للقلق على إدارة الذاكرة.
وهذه هي النقطة التي بدأنا منها في الأسبوع الأول، فقط باستخدام المصفوفات على المكدس،
إذا جاز التعبير.
ولم نستخدم malloc مطلقًا.
إذن الفكرة فقط أنه، بمجرد أن تبدأ في استخدام malloc، ودالة free،
والذاكرة بشكل عام، سيقع على عاتقك المزيد من المسؤوليات
أكثر مما فعلنا في الأسبوع الأول.
سؤال جيد.
أي أسئلة أخرى؟
حسنًا.
إذن، تبين أن، هناك أداة أخرى، بكل جدية.
ها هي ذا.
[? GDB50. ?] إذن debug50 هو إشارة إلى أداة شائعة جدًا، تسمى، GDB50،
مصحح الأخطاء [؟ Gnu؟].
إنها الأداة الأقدم التي لن تستخدمها في سطر الأوامر،
ولكنها تجعل debug50 تعمل.
اتضح أنه، يوجد شيء.
وهناك بالفعل مقال ويكيبيديا فعلي
ربما قد فتحته في البريد الإلكتروني الخاص بي ليلة أمس، يطلق عليه تصحيح أخطاء البطة المطاطية.
وبصراحة، لا يتعين عليك القيام بكل شيء، بشكل مفرط، كما فعلنا هنا،
لكن الغرض من هذه التقنية، من برنامج تصحيح أخطاء البطة المطاطية،
هو الحفاظ، حرفيًا، على البطة المطاطية على الرف لديك، أو على مكتبك.
وعندما تواجه خطأ وليس لديك زميل تدريس،
أو رفيق سكن حصل على دورة CS50، أو صديق فني آخر يمكنه مساعدتك في الانتقال
خلال تعليمتك البرمجية، حرفيًا، فابدأ الانتقال خلال تعليمتك البرمجية
حرفيًا، متحدثًا إلى البطة قائلاً، حسنًا، على السطر 2، أنا أعلن main،
وعلى السطر 3، أقوم بتخصيص مساحة لمصفوفة.
ثم، على السطر 4، أستدعي - آه!
هذا ما أفعله بشكل خاطىء.
إذن إذا كان أي منكم قد تعرّض لذلك، سواء في ساعات العمل،
أو بمفردك، حيث تتحدث داخل عقلك،
أو كنت تتحدث من خلال تعليمتك البرمجية إلى شخص آخر.
وهنا، لا يتعين عليها الرد.
أنت فقط تستمع إلى نفسك وأنت تقول الشيء الخاطئ، أو تتعرّض لذلك.
يمكنك تقريب ذلك من خلال الاحتفاظ بواحدة من صغار البط تلك على مكتبك،
والإبقاء على تلك المحادثة.
ولا يبدو هذا جنونيًا كما هو في الواقع.
إنه عبارة عن عملية تحدّث فقط خلال تعليمتك البرمجية بشكل منطقي،
خطوة خطوة، وبطريقة لا يمكنك بالضرورة القيام بها داخل عقلك.
على الأقل لا يمكنني.
عندما تسمع نفسك وأنت تقول شيئًا خاطئًا،
أو لا يمكنك اتباعها بشكل منطقي، وفجأة، يمكنك
الحصول على ذلك في الواقع.
إذن في نهاية اليوم، بكل الوسائل، خذ أيًا من صغار البط هذه.
حيث استغرق ذلك وقتًا طويلاً من [? كولتن ?] لوضعها اليوم.
وسيكون لدينا المزيد في ساعات العمل في الأسابيع المقبلة، إذا كنتم ترغبون في ذلك.
إذن قد يتذكر البعض منكم تلك البطة من منزل [؟ كورير؟]
العام الماضي أيضًا، والتي كانت ابنة عم لخاصته أيضًا.
حسنًا.
إذن هذا برنامج تصحيح أخطاء البطة المطاطية.
الآن، الأسبوع الماضي، تذكرون أننا بدأنا في إخراج عجلات التدريب.
التي كنا سنستخدمها لبضعة أسابيع، المكتبة CS50.
ويبدو هذا من الماضي الآن.
كانت هذه مجرد تقنية، أداة، عبرها
يمكننا أن نحصل على مدخل المستخدم بطريقة أفضل، مما لو بدأنا بالفعل
بالتعامل مع الذاكرة في وقت مبكر.
وكشفنا الأسبوع الماضي أن "سلسة"، اقتباس، إنهاء الاقتباس،
عبارة عن ماذا، أسفل الغطاء في لغة C؟
قلها مجددًا.
مصفوفة من الأحرف.
وبصورة أكثر تحديدًا، إنها S-T-R-I-N-G مرادفة لأي نوع
من البيانات بالفعل؟
char نجمة، كما أطلقنا عليها.
إذن char نجمة هي مجرد طريقة يصف بها علماء الكمبيوتر
مؤشرًا إلى حرف،
أو بالأحرى عنوان حرف، وهي
تعادل قول مصفوفة الذاكرة، أو تسلسل الذاكرة بشكل وظيفي.
ولكنها طريقة فنية أكثر، وأكثر دقة لوصفها.
ونعلم أن لدينا الآن char نجمة أسفل الغطاء، حسنًا،
من أين يأتي كل هذا؟
حسنًا، في الحقيقة، يتم ربطه مباشرة بتلك الذاكرة.
نستمر في الإشارة إلى أن شيئًا مثل هذا داخل جهاز الكمبيوتر الخاص بك.
ويمكننا التفكير في الذاكرة باعتبارها مجرد أجزاء من الذاكرة،
جميع وحدات البايت بها مرقمة.
من 0 إلى 2 جيجابايت، أو 2 مليار، أيًا كانت القيمة.
ولكن بالطبع الأسبوع الماضي، أشرنا إلى أنك تفكر في هذه الذاكرة
ليس كجهاز في حد ذاتها، ولكن كمجموعة الذاكرة تلك التي
تنقسم إلى مناطق مختلفة.
الجزء العلوي من ذاكرة الكمبيوتر الخاص بك، إذا جاز التعبير،
هو ما نطلق عليه جزء النص.
وماذا يحدث في جزء النص من ذاكرة جهاز الكمبيوتر
عندما تقوم بتشغيل برنامج؟
يبدو النص كاختيار ضعيف للكلمات، بصراحة، ولكن ما هو؟
قلها مجددًا.
الجمهور: رؤوس الملفات؟
ديفيد ج. مالان: ليست رؤوس الملفات، في هذه الحالة.
هذا في سياق تشغيل برنامج، وليس بالضرورة حفظ ملف.
نعم؟
الجمهور: حرفية السلسلة.
ديفيد ج. مالان: ليست حرفية السلسلة هنا،
لكنها قريبة، بالفعل، في الذاكرة.
الجمهور: الدوال.
ديفيد ج. مالان: الدوال، اقتربنا.
نعم.
أين يوجد جزء النص من ذاكرة جهاز الكمبيوتر،
عندما تنقر بشكل مزدوج فوق برنامج ليتم تشغيله،
أو في Linux، عندما تكتب نقطة خط مائل شيئًا ما، ليتم تشغيله.
حيث توجد أصفار وواحدات برنامجك الفعلي، التعليمة البرمجية الآلية،
التي تحدثنا عنها في الأسبوع صفر، يتم تحميلها فقط في ذاكرة الوصول العشوائي.
لذلك تذكرون أنه من الأسبوع الماضي، كما تعلمون، أي شيء مادي في هذا العالم--
محركات الأقراص الصلبة، محركات الأقراص ذات الحالة الصلبة، بطيء.
لذا تلك الأجهزة بطيئة، ولكن ذاكرة الوصول العشوائي، والأشياء التي واصلنا سحبها على الشاشة،
سريعة نسبيًا.
إذا كانت فقط لا تحتوي على أجزاء متحركة.
إنها إلكترونية تمامًا.
إذن عند النقر المزدوج فوق برنامج على جهاز Mac أو الكمبيوتر الشخصي لديك،
أو كتابة نقطة خط مائل شيء في Linux، الذي
يتم تحميله من جهاز بطيء، محرك الأقراص الصلبة لديك،
حيث يتم تخزين البيانات لمدة طويلة، في ذاكرة الوصول العشوائي أو الذاكرة،
حيث يمكنها أن تعمل بسرعة أكبر بكثير وبصورة ممتعة من حيث الأداء.
إذن، ماذا يعني هذا في الواقع لنا؟
حسنًا، لقد انتقلت إلى مكان ما.
لقد قررنا، نحن البشر، فقط، منذ سنوات أنها
ستنتقل إلى أعلى، إذا جاز التعبير، هذا الجزء من الذاكرة.
وتوجد في الأسفل على الرغم من ذلك، المناطق الأكثر ديناميكية من الذاكرة--
المكدس والكومة.
وقلنا هذا منذ لحظات، وفي الأسبوع الماضي كذلك، ما الذي يجري في الكومة؟
أو مَن يستخدم الكومة؟
الجمهور: الذاكرة الديناميكية.
ديفيد ج. مالان: الذاكرة الديناميكية.
أي وقت تستدعي malloc، فأنت تطلب من نظام التشغيل
ذاكرة مما يطلق عليه الكومة.
في أي وقت تستدعي دالة free، فأنت تقوم بإرجاعها مجددًا من الناحية النظرية.
على سبيل المثال، هي لا تنتقل في الواقع إلى أي مكان.
أنت تقوم فقط بتمييزها كمتاحة للمتغيرات والدوال الأخرى ليتم استخدامها.
في الوقت نفسه، فيمَ يتم استخدام المكدس؟
الجمهور: المتغيرات المحلية.
ديفيد ج. مالان: المتغيرات المحلية وأي من الدوال الخاصة بك.
إذن main، تأخذ عادة شريحة من الذاكرة في الأسفل.
إذا استدعت main دالة أخرى، فستحصل على شريحة من الذاكرة أعلى ذلك.
إذا كانت هذه الدالة تستدعي واحدة أخرى، فستحصل على شريحة من الذاكرة أعلى ذلك.
حتى يكون لكل منها مناطق مختلفة خاصة بها من الذاكرة.
ولكن بالطبع، السهمان، يشيران إلى بعضهما البعض،
لا يبدو هذا تصميمًا جيدًا.
ولكن في الواقع، يمكن أن تحدث أشياء سيئة.
يمكنك تخصيص الكثير من تلك الذاكرة، وفجأة، يتجاوز المكدس الكومة.
أو تتجاوز الكومة المكدس.
وهكذا تم إنشاء مواقع ويب مثل Stack Overflow، وما شابه ذلك.
لكن ذلك واقع تمامًا.
إذا كانت لديك كمية محدودة من الذاكرة، في مرحلة ما،
سيتعطل شيء ما.
أو سيتعين على الكمبيوتر قول، امم-امم، لا يوجد المزيد من الذاكرة.
سيتعين عليك إنهاء بعض البرامج، أو إغلاق بعض الملفات،
أو أي شيء.
لذا كان هذا فقط لقول أن هذه هي الكيفية التي يتم وضع الذاكرة بها.
وبدأنا في استكشاف هذا عن طريق بعض البرامج.
هذا البرنامج الموجود هنا-- إنه داكن قليلاً هنا.
وهذا الموجود هنا، كان دالة swap.
الآن إنه داكن أكثر.
كانت دالة swap في الواقع هي التي تبدّل بين القيمتين، A وB.
لكنها في الواقع لم تعمل بالطريقة التي قصدناها.
ما الذي تم تقسيمه في دالة swap تلك الأسبوع الماضي؟
على سبيل المثال، أنا متأكد من أنها نجحت.
وعندما صعدت متطوعتنا الشجاعة وبادلت عصير البرتقال والحليب،
نجح الأمر.
إذن، كان المنطق صحيحًا، ولكن البرنامج نفسه لم ينجح.
لماذا؟
الجمهورر: لقد تغيرت قيم نُسخ المتغيرات.
ديفيد ج. مالان: بالضبط.
غيرت القيم في نُسخ المتغير.
إذن تذكرون، أنه عندما كانت main الدالة
التي استدعيناها، وكانت لديها قيمتان، x وy، كان ذلك الجزء من الذاكرة هنا.
كان هذا الجزء من الذاكرة هنا.
وكان به مثل الرقمين 1 و2.
ولكن عندما استدعى دالة swap، التي حصلت على جزء الذاكرة الخاصة بها.
لذا كانت main في الأسفل، وكانت دالة swap أعلى ذلك.
ولديها أجزاء الذاكرة الخاصة بها والتي يطلق عليها، a وb، والتي
حصلت، في البداية على القيمتين 1 و2.
تمت مبادلة 1 و2 بالفعل بنجاح،
ولكن هذا لم يؤثر على x وy.
لذا قمنا بإصلاح ذلك.
مع الإصدار الأحدث من هذا البرنامج، بالطبع،
بدا مشفرًا أكثر للوهلة الأولى، ولكن في اللغة الإنجليزية،
هل يمكن أن يصف شخص ما تحديدًا ما يحدث
في هذا المثال الذي كان صحيحًا بدرجة أكبر؟
على سبيل المثال، ما الذي يفعله هذا البرنامج سطرًا بسطر؟
نعم؟
الجمهور: بدلاً من تمرير نُسخ المتغيرات،
تقوم بتمرير المؤشرات إلى عناوينها.
ديفيد ج. مالان: بالضبط.
بدلاً من تمرير قيم المتغيرات، وبالتالي نسخها،
إنها تقوم بتمرير عناوين تلك المتغيرات.
لذا هذا يبدو مثل قول، لا يهمني من الناحية الفنية مكانها في الذاكرة،
ولكن أحتاج إلى أن أعرف أنها في مكان ما في الذاكرة.
لذا بدلاً من تمرير x في الرقم 1،
لنفترض أن x في الموقع 100--
دعوني أنتقل إلى مثال.
إنه في الواقع الرقم 100 الذي سنضعه هناك.
وإذا كان y فى الموقع على سبيل المثال، 104، حسنًا، فإن
104 هو الرقم الذي سنضعه هناك، وهو ليس القيمة التي نريد تبديلها،
لكن هذه بمثابة خريطة صغيرة أو تتبع للآثار إذا صح التعبير،
التي تقودنا إلى الموقع الصحيح.
حتى عندما ننفذ تلك التعليمات البرمجية، التي نبدلها فى النهاية
في السطور الثلاثة تلك، وهي هذا وهذا، وجميعها على طول الطريق،
تذكرون، أننا نستخدم متغيرًا مؤقتًا هناك
يمكننا الاستغناء عنه بعد ذلك.
لذا هذا ما تتيحه لنا المؤشرات للقيام به.
و هذا ما يتيح لنا تغيير القيم بالفعل على ما يسمى بالمكدس،
حتى باستدعاء دالة أخرى.
حسنًا.
أي أسئلة لاحقًا، بشأن مكان توقفنا في المرة السابقة عند حديثنا عن المكدس والمبادلة؟
لا؟
حسنًا.
إذن تذكرون أننا قدمنا بينكي أيضًا الذي فقد رأسه عند نقطة معينة،
لكن لماذا؟
ما الخطأ المروع الذي حدث، وانحرف بصورة مروعة في هذا المشهد من فيلم الأسبوع الماضي
من ستانفورد؟
كان بينكي يقوم بكل شيء على نحو صحيح، أليس كذلك؟
مثل، تحريك القيم.
كان 42 ناجحًا.
ومن ثم، أجل؟
الجمهور: حاول القيام بالإسناد المؤشري لشيء
لم يكن يشير إلى أي عنوان حقيقي.
ديفيد ج. مالان: بالضبط.
حاول القيام بالإسناد المؤشري لمؤشر، لعنوان، لم يكن يشير بالفعل
إلى عنوان صالح.
تذكرون أن هذا كان هو السطر الموجود في التعليمة البرمجية المطلوبة والذي كان سيئًا وغير موفق.
نجمة y، تعني، انتقل إلى العنوان في y، وقم بشيء ما له.
قم بتعيينه ليصبح معادلاً للرقم 13.
ولكن كانت المشكلة، في التعليمة البرمجية، التي تحدثنا عنها الأسبوع الماضي
أن كل ما فعلناه في البداية كان قول مرحبًا، جهاز الكمبيوتر أعطني مؤشرًا لعدد صحيح،
وأطلق عليه x.
قم بنفس الأمر، وأطلق عليه y.
خصّص مساحة وضَع x فيها.
لكننا لم نقم بالأمر نفسه لـ y.
إذن في حين تضمن x، الأسبوع الماضي، عنوان جزء حقيقى من الذاكرة،
بفضل malloc، ما الذي يتضمنه y في تلك المرحلة من القصة؟
السطر الأصفر هناك.
ما الذي يتضمنه y؟
أي قيمة؟
الجمهور: فارغ.
ديفيد ج. مالان: فارغ.
ربما.
لكنه ليس واضحًا لعدم الإشارة إلى فارغ في البرنامج.
قد نكون محظوظين.
فارغ هو فقط 0.
وأحيانًا نرى أن 0 هو القيم الافتراضية في برنامج.
إذن ربما.
لكن أنا أقول، ربما، وأنا أحاول إعطاء إجابة قاطعة عن السبب.
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: حسنًا.
و لم تقم بتخصيصه-- حسنًا، تخصيص، ليست الكلمة الصحيحة تمامًا.
هذا يقترح أنك قمت بتخصيص الذاكرة الفعلية.
إنها قيمة ضئيلة.
يوجد شيء ما هناك.
أليس كذلك؟
تم تشغيل جهاز Mac الخاص بي لبضع ساعات.
ومن المحتمل أن تعمل أجهزة Mac، وأجهزة الكمبيوتر الشخصي، والهواتف الخاصة بكم، طوال اليوم.
أو بالتأكيد عندما يكون الغطاء مفتوحًا.
وهكذا، يتم استخدام الذاكرة وإلغاء استخدامها، واستخدامها.
على سبيل المثال، تحدث أشياء كثيرة.
إذن لم تتم تعبئة جهاز الكمبيوتر الخاص بك بجميع الأصفار والواحدات.
إذا نظرت إليها في بعض المراحل العشوائية في اليوم،
إنها ممتلئة بمجموعات ومجموعات من الأصفار والواحدات
من البرامج السابقة التي خرجت منها منذ فترة طويلة.
النوافذ الموجودة لديك فى الخلفية وما شابه ذلك.
إذن، اختصارًا لها، عندما تقوم بتشغيل
برنامج للمرة الأولى، ويتم تشغيله الآن لبعض الوقت،
سيصبح فوضويًا.
سيكون بهذا المستطيل الكبير من الذاكرة هنا بعض الواحدات
وبعض الأصفار هنا والعكس.
لذلك هي قيم ضئيلة، لأن وحدات البايت هذه بها بعض القيم.
أنت لا تعرف بالضرورة ماهيتها.
إذن النقطة هي، لا يجب أن تقوم بالإسناد المؤشري أبدًا لمؤشر
لم تقم بتعيينه بنفسك.
قد يتعطل.
وقد لا يتعطل.
يمكن أن يساعدك Valgrind في إيجاد تلك الأشياء ولكن يتم ذلك أحيانًا.
لكنها ليست عملية آمنة فقط.
وأخيرًا، كان آخر شيء قدمناه الأسبوع الماضي،
والذي سيكون ركيزة للمشاكل التي سنقوم بحلها هذا الأسبوع،
هو struct.
إذن struct نوعًا ما رائع، حيث يمكنك تصميم بنيات بياناتك
المخصصة.
تبدو لغة C محدودة للغاية خارج الصندوق، إذا جاز التعبير.
لديك فقط chars وpools، وfloats، وints، وdoubles،
وlongs، وstr--
حسنًا، ليس لدينا سلاسل، في حد ذاتها.
لذا فهي لا تأتي بميزات كثيرة بالفعل، كالكثير من اللغات.
مثل Python، والتي سنراها في غضون بضعة أسابيع.
إذن باستخدام struct في لغة C، لديك القدرة على
حل بعض المشاكل الخاصة بك.
على سبيل المثال، باستخدام struct، يمكننا بالفعل
البدء في تنفيذ الميزات الخاصة بنا.
أو أنواع البيانات الخاصة بنا.
على سبيل المثال، دعوني أنتقل إلى الأعلى هنا.
ودعوني أمضي قدمًا وأقوم بإنشاء ملف يسمى مثلاً،
student، أو بالأحرى struct نقطة h.
إذن تذكرون أن نقطة h هو ملف رأس.
حتى الآن، كنتم تستخدمون ملفات الرأس التي قام أشخاص آخرون بإنشائها.
مثل، CS50 نقطة h، وstandard IO نقطة h، وstandard [? lid ?] نقطة h،
لكن يمكنكم إنشاء واحد لكم.
ملفات الرأس هي مجرد ملفات تحتوي عادةً على تعليمات برمجية
ترغب في مشاركتها عبر برامج متعددة.
وسنرى المزيد من ذلك في الوقت المناسب.
لذا دعوني أمضي قدمًا وأقوم فقط بحفظ هذا الملف.
ولنفترض أنني أريد تمثيل طالب في الذاكرة.
فإن الطالب بالطبع، من المحتمل أن يكون لديه ماذا؟
على سبيل المثال، ماذا عن سلسلة لاسمه،
سلسلة لسكنه-- ولكن السلسلة منذ أسبوعين تقريبًا.
دعونا نسمي هذا char نجمة.
ودعونا نطلق عليه اسم، char نجمة.
لذا قد ترغب في الربط هكذا، قطع بيانات متعددة مع الطلاب.
أليس كذلك؟
ولا ترغب في أن يكون لديك متغيرات متعددة، في حد ذاتها.
سيكون من الجيد تغليف هذه معًا.
وتذكروا أننا في نهاية الأسبوع الماضي قد
شاهدنا هذه الميزة حيث يمكنكم تحديد النوع الخاص بكم،
باستخدام typedef، والذي يمثل بنية بنفسها.
ويمكنكم إطلاق اسم عليه.
لذا باختصار، وببساطة بتنفيذ سطور التعليمة البرمجية هذه،
قمتم بإنشاء نوع البيانات المخصصة الخاصة بكم للتو.
والتي تسمى student الآن.
ويجب أن يكون لدى كل طالب في العالم، حسب هذه التعليمة البرمجية، اسم
وسكن مرتبطان به.
الآن، لماذا يعدّ هذا مفيدًا؟
حسنًا البرنامج، الذي نظرنا إليه في نهاية المرة السابقة ظهر
شيئًا صغيرًا كهذا.
Instruct0 نقطة c، كان لدينا ما يلي،
أنا أولاً قمت بتخصيص مقدار من المساحة للطالب.
سألتُ المستخدم ما هو التسجيل في الصف أو ما هو غير ذلك؟
ويعطينا هذا عددًا صحيحًا.
ومن ثم، قمنا بتخصيص مصفوفة من النوع student، تسمى students، جمع.
كان هذا بديلاً، تذكرون، للقيام بشيء ما
كهذا، string names enrollment، وstring dorms enrollment.
أيهما سينجح.
يمكن أن تكون لديك مصفوفتان منفصلتان، ويجب عليك فقط
أن تتذكر الاسم صفر أو المسكن صفر هو نفس الشخص.
ولكن لماذا تقوم بذلك إذا كان بإمكانك الاحتفاظ بالأشياء معًا.
إذن باستخدام structs، تمكننّا من القيام بذلك.
أعطني العديد من بنيات الطلاب، وأطلق على المصفوفة كاملة اسم students.
والصيغة الجديدة الوحيدة التي نقدمها لتحقيق هذا الهدف، كانت أي مُشغل؟
الجمهور: النقطة.
ديفيد ج. مالان: النقطة.
أجل.
لذا في الماضي، تذكرون أننا منذ أسبوعين تقريبًا، قدمنا ​​المصفوفات.
وتتيح لك المصفوفات القيام بتدوين الأقواس المربعة.
لذا هذا لا يختلف عمّا حدث في الأسبوعين الماضيين.
لكن إذا لم تخزِّن مصفوفتك الأعداد الصحيحة أو الأحرف أو القيم الكسرية فقط،
أو أيًا كان، فإنها تخزِّن بالفعل بنية، مثل student،
يمكنك الحصول على اسم ذلك الطالب حرفيًا بقول نقطة name فقط.
ويمكنك الحصول على مسكنه من خلال القيام بكتابة نقطة dorm.
ومن ثم أي شيء آخر على نفس الحال.
وهذا ما يسمى، التغليف.
وهو يعتبر نوعًا ما كمبدأ أساسي في البرمجة
وفيه، إذا كان لديك كيان ما في العالم الحقيقي، مثل طالب،
وتريد تمثيل طلاب باستخدام التعليمة البرمجية، أجل،
فيمكن أن تكون لديك مجموعة من المصفوفات التي أطلق عليها جميعًا أسماء، مساكن، عناوين بريد إلكتروني، أرقام
هواتف، لكن يصبح هذا فوضويًا تمامًا.
يمكنك بدلاً من ذلك تغليف كل تلك المعلومات ذات الصلة حول طالب
في بنية بيانات واحدة بحيث يكون لديك الآن، حسب الأسبوع صفر، تجريد.
على سبيل المثال، الطالب هو تعبير تجريدي.
وإذا ما كسرنا هذا التجريد، فما هو الطالب في الواقع؟
ليس في العالم الحقيقي، لكن في عالم التعليمات البرمجية الخاص بنا هنا؟
الطالب هو تعبير تجريدي.
إنها كلمة مفيدة، يمكن لنا جميعًا تقريبًا التوافق على أنها تعني شيئًا ما،
لكن من الناحية الفنية، ماذا يعني هذا بوضوح؟
الطالب هو في الواقع اسم في مسكن، وهو في الواقع نوعًا ما
صغير بالنسبة للجميع في هذه الغرفة، ولكننا قمنا باستخلاصه في التعليمة البرمجية
لتلك القيمتين فقط.
لذا لدينا هنا تغليف.
تقوم تقريبًا بتغليف قيم متعددة معًا.
وتقوم بالتجريد ليكون لديك فقط مصطلح أكثر فائدة،
لأنه لا أحد سيرغب في التحدّث في مصطلحات سطور التعليمة البرمجية
لوصف أي شيء.
لذا، نفس الموضوع كما حدث في الماضي.
لذا، لدينا الآن القدرة على التوصل إلى بنيات البيانات المخصصة الخاصة بنا
كما تبدو.
يمكننا تخزين أي شيء نريده داخلها.
لذا دعونا نرى الآن كيف قمنا بتصميم
بعض الأشياء بشكل رديء في الأسابيع القليلة الماضية.
لذا يتضح أن معظم التعليمات البرمجية، التي آمل
أننا كتبناها في الأسابيع الأخيرة بطريقة صحيحة،
لكننا لم نصمم بالضرورة حلولاً بطريقة أفضل.
تذكرون أنه عندما يكون لدينا هذا الجزء من الذاكرة،
فإننا تعاملنا تمامًا معه في الغالب، كمصفوفة.
لذا جزء من الذاكرة متجاور فقط.
وبفضل هذا النموذج العقلي البسيط جدًا، نحصل على السلاسل،
نحصل على مصفوفات الطلاب الآن.
ولكن المصفوفات ليست بالضرورة بنية البيانات الأفضل في العالم.
على سبيل المثال، ما هو الجانب السلبي لمصفوفة إذا واجهتها حتى الآن.
في لغة C، ما هو الجانب السلبي للمصفوفة؟
أجل؟
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: هل يمكن أم لا يمكن؟
الجمهور: لا يمكن.
ديفيد ج. مالان: لا يمكنك.
هذا صحيح.
إذن في لغة C، لا يمكنك خلط أنواع البيانات داخل مصفوفة.
يجب أن تكون جميعها أعداد صحيحة، يجب أن تكون جميعها أحرفًا، يجب أن تكون جميعها طلابًا.
إنها كذبة بيضاء لأنه من الناحية الفنية، يمكنك
الحصول على شيء ما يسمى void star، ويمكنك بالفعل الربط-- لكن أجل.
على الرغم من أن ذلك صحيح، وعلى وجه التحديد-- لا يمكن خلط أنواع البيانات.
بصراحة، حتى على الرغم أن بعض اللغات الأخرى تتيح لك القيام بذلك،
فهي ليست بالضرورة أفضل قرار تصميم.
لكن بالتأكيد، إنها حد.
أفكار أخرى.
أجل؟
الجمهور: لا يمكن تغيير الحجم.
ديفيد ج. مالان: لا يمكن تغيير الحجم.
دعونا نركز على هذا.
لأن هذا نوع من الحد بشكل أكبر مما يبدو.
لذا إذا كنت تريد مصفوفة من أجل، لنقل، قيمتين، فما الذي ستقوم به؟
حسنًا، يمكنك القيام بشيء مثل int، x، قوس، 2، فاصلة منقوطة.
وما الذي سيعطيه لك ذلك بالفعل داخل ذاكرة جهاز الكمبيوتر الخاص بك؟
سيعطيك جزءًا ما سنقوم برسمه كمستطيل.
هذا هو الموقع 0.
هذا هو الموقع 1.
لنفترض أنك، أوه، بعد بضع دقائق، غيرتَ رأيك.
أوه، لقد أخذتُ للتو--
أريد الكتابة في قيمة ثالثة، أو أريد
إضافة طالب آخر إلى المصفوفة.
أين ستضع هذا؟
حسنًا، لن تفعل.
إذا كنت ترغب في إضافة قيمة ثالثة إلى مصفوفة من الحجم 2،
فما هو خيارك الوحيد في لغة C؟
الجمهور: إنشاء مصفوفة جديدة.
ديفيد ج. مالان: ستقوم بإنشاء مصفوفة جديدة.
هكذا حرفيًا.
وإذا كانت المصفوفة تحتوي على رقم مثل 42،
وهذه تحتوي على الرقم 13، فإن الطريقة الوحيدة لإضافة رقم ثالث هو تخصيص
مصفوفة ثانية، ونسخ القيم إلى نفس الموقعين، 42، 13، وبعد ذلك،
سنقوم بإضافة قيمة أخرى، 50.
ومن ثم، حتى لا تستخدم ما يزيد عن ضعفي المساحة
بشكل دائم تقريبًا، الآن تستطيع التحرر بطريقة ما تقريبًا،
أو التوقف عن استخدام هذا الجزء من الذاكرة.
لذا هذا جيد.
إنه فقط تصحيح لما قمنا به.
ولكن ما هي مدة تشغيل هذه العملية؟
تذكرون أننا منذ أسبوعين، بدأنا في التحدث عن الكفاءة والتصميم.
ما هي مدة التشغيل لتغيير حجم مصفوفة.
الجمهور: طويل للغاية.
ديفيد ج. مالان: كررها مرة أخرى.
الجمهور: قلتُ، طويل للغاية.
ديفيد ج. مالان: طويل للغاية.
جيد.
لكن دعونا نكون أكثر دقة.
حرف o الكبير في-- حرف o الكبير فيمَ؟
الجمهور: N.
ديفيد ج. مالان: N. ما هو n؟
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: حسنًا.
صحيح.
لكن ما الذي يمثله n؟
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: أجل.
لذا فأنتم في الواقع لم يعُد هناك داعٍ لعدم معرفتكم بذلك.
إنها مجرد إجابة عامة.
في هذه الحالة، فأيًا كان طول المصفوفة، أطلقوا عليها n.
إنها خطوات متعددة لتغيير حجمها إلى هذا زائد 1.
من الناحية الفنية هي رمز o الكبير، على n، زائد 1.
لكن تذكروا أننا في مناقشتنا، "تدوين رمز o الكبير"، أننا فقط
تجاهلنا المصطلحات الأصغر-- زائد الواحدات، المقسومة على الاثنينات، زائد n.
نحن نركز فقط على المصطلح الأقوى في التعبير، وهو
تحديدًا n هنا.
إذن أجل، إذا كانت لديك مصفوفة بحجم 2، وقمت بتغيير حجمها
إلى مصفوفة بحجم 3 أو في الواقع، n زائد 1، فهذا
سيستغرق مني n من الخطوات.
من الناحية الفنية n زائد 1 من الخطوات.
لكن n من الخطوات.
وهذا ما يشير إليه حرف o الكبير من n.
لذا هي عملية خطية.
ممكنة جدًا ولكنها ليست بالضرورة أسرع
شيء لأنه كان عليه حرفيًا أن يحرك كل تلك القيم التافهة.
إذن ما الذي سيكون أفضل من هذا؟
وإذا قمت بالبرمجة من قبل، فقد يكون لديك الحدس الصحيح بالفعل.
كيف يمكننا حل هذه المشكلة؟
أجل؟
الجمهور: هل يمكنك تخصيص المزيد من الذاكرة في نهاية المصفوفة؟
ديفيد ج. مالان: إعادة تخصيص المزيد من الذاكرة في نهاية المصفوفة.
إذن من الواضح أن لغة c تحتوي على دالة تسمى، realloc.
تمامًا، إذا لم تتم تسميتها بشكل واضح وهي تعيد تخصيص الذاكرة.
وإذا قمت باجتيازها، فإن عنوان جزء الذاكرة الذي قمتَ بتخصيصه،
وسيلاحظ نظام التشغيل، أوه، نعم أنك كنت محظوظًا.
لدي المزيد من الذاكرة في نهاية هذه المصفوفة،
ستقوم بعد ذلك بتخصيص ذاكرة الوصول العشوائي الإضافية تلك لك، وتتيح لك استخدامها.
أو في أسوأ حالة، إذا لم يتوفر أي شيء في نهاية
المصفوفة الموجودة في الذاكرة، لأنه يتم
استخدامها بواسطة شيء ما آخر في برنامجك.
هذا جيد.
ستتولى Realloc مسؤولية إنشاء مصفوفة أخرى في مكان ما
في الذاكرة، ونسخ جميع بياناتك تلك فيها،
وإرجاع عنوان الجزء الجديد من الذاكرة هذا.
لسوء الحظ، هذا لا يزال خطيًا.
أجل؟
الجمهور: هل يتم كل هذا في الكومة؟
أو--
ديفيد ج. مالان: يتم كل هذا في الكومة.
Malloc، وrealloc، وfree، جميعها تعمل على الكومة.
أجل.
إذن هذا حل، لكنه لا لا يعبر بالفعل عن الكفاءة.
أجل؟
الجمهور: هل يمكنك استخدام قائمة مترابطة؟
ديفيد ج. مالان: أجل.
ما هي القائمة المترابطة؟
امضِ قدمًا.
الجمهور: عندما يكون لديك عنصر يشير إلى عناصر مختلفة.
ديفيد ج. مالان: حسنًا.
تشير إلى عناصر أخرى.
أجل.
لذا دعوني أتحدث عن المشكلة الأساسية هنا.
المشكلة الأساسية هي أشبه بأن تضع نفسك في موقف حرج،
لذا إذا جاز التعبير، كما يقول المثل الشائع.
باستخدام مصفوفة، فأنت تقرر مسبقًا ما هو حجم بنية البيانات
وتلتزم به.
حسنًا، ماذا لو فعلت العكس فقط.
لا تفعل ذلك.
إذا كنت ترغب في ذلك من البداية، فخصّص مساحة لقيمة واحدة فقط، لنقل عدد صحيح واحد،
فقط اطلب من الكمبيوتر القيام بذلك.
أعطني مساحة لعدد صحيح واحد وسأضع رقمي 42 هنا.
وبعد ذلك، إذا وفقط إذا، كنت ترغب في عدد صحيح ثانٍ،
فاطلب من الكمبيوتر عددًا صحيحًا ثانيًا.
وهكذا فإن الكمبيوتر، كما هو الحال من خلال malloc، أو أي شيء، سيعطيك واحدًا آخر
على سبيل المثال، الرقم 13.
وإذا كنت ترغب في الحصول على ثالث، فاطلب نفس الطلب من نظام التشغيل.
في كل مرة فقط يعيد فيها جزءًا واحدًا من الذاكرة.
لكن يوجد مفهوم أساسي هنا.
هناك دائمًا عملية مقايضة.
إذن أجل، هذا ممكن.
يمكنك استدعاء malloc ثلاث مرات.
في كل مرة تطلب جزءًا من الذاكرة بحجم 1، بدلاً من الحجم 3،
على سبيل المثال.
لكن ما هو المقابل الذي ستقدمه؟
أو ما هي المشكلة التي ما زلنا بحاجة إلى حلها؟
أجل؟
الجمهور: لا يتم تخزينها بجانب بعضها البعض.
ديفيد ج. مالان: أجل.
لا يتم تخزينها بجانب بعضها البعض.
على الرغم من ذلك، يمكنني التفكير في هذا كالعنصر الأول، والثاني،
والثالث، فليس لديك في هذه القصة، الوصول العشوائي إلى العناصر.
والوصول العشوائي، وهذا ما يشار إليه، بذاكرة الوصول العشوائي، أو RAM،
يعني هذا فقط بشكل حسابي، على سبيل المثال، أنه يمكنك
الانتقال، بشكل حسابي، إلى الموقع 0، الموقع 1، الموقع 2، بشكل عشوائي، أو في وقت
ثابت.
فقط بشكل مباشر.
لأنهم إذا عادوا جميعًا على التوالي إلى الوراء، فإن كل ما عليك القيام به هو،
إضافة 1، أو إضافة 4، أو أي شيء إلى العنوان، وأنت هناك.
لكن المشكلة هي، أنك إذا قمت باستدعاء malloc مرارًا
وتكرارًا، فلا يوجد ضمان بأن هذه الأشياء
ستكون قريبة من بعضها البعض.
قد تنتهي هذه الأجزاء الثانية من الذاكرة--
إذا كان هذا جزءًا كبيرًا من الذاكرة التي تحدثنا عنها،
حيث الأكوام بالأعلى هنا، والمكدسات بالأسفل هنا--
فقد ينتهي 42 هنا.
الجزء التالي من الذاكرة، 50، قد ينتهي هنا.
الجزء الثالث قد ينتهي هنا.
لذا لا يمكنك الانتقال من الموقع 0، إلى 1، إلى 2،
لأنه يجب عليك أن تتذكر بطريقة ما أين يوجد الموقع 0، و1، و2.
إذن كيف يمكننا حل هذا؟
حتى لو لم تقم بالبرمجة من قبل، على سبيل المثال، ما هو الحل هنا؟
الجمهور: بطريقة ما تخزين [INAUDIBLE].
ديفيد ج. مالان: حسنًا.
التخزين بطريقة ما لعناوين--
الجمهور: من [INAUDIBLE]
ديفيد ج. مالان: حسنًا.
لذلك دعونا نفترض، من أجل المناقشة، أن هذا الجزء من الذاكرة
ينتهى في الموقع 100.
هذا الجزء ينتهى في 150 تقريبًا.
هذا الجزء ينتهى في 475.
مهما كانت تلك القيم.
يبدو أنني بطريقة أو بأخرى أحتاج إلى تذكر القيم الثلاث--
100، و150، و475.
لذا أين يمكنني تخزين ذلك؟
حسنًا، من الواضح أنه، يمكنني أن أكون ماهرًا قليلاً ولكن طماعًا قليلاً.
أستطيع أن أقول لـ malloc، تعرف ماذا، في كل مرة أقوم باستدعائك فيها، لا تعطيني
فقط مساحة لعدد صحيح، أعطني مساحة لعدد صحيح
بالإضافة إلى عنوان عدد صحيح آخر.
لذا إذا رأيت ما يشبه حبات الفشار المصطفة معًا في سلسلة،
أو أي نوع من سياج ربط السلسلة حيث يرتبط أحد الروابط بالآخر.
فيمكننا إنشاء معادل لـ-- عفوًا ليس هذا.
يمكننا إنشاء معادل لنوع الصورة هذا،
حيث كل هذه المربعات أو العُقد، سنبدأ بتسميتها، نوعًا من روابط
مرتبطة بيانيًا ببعضها البعض.
حسنًا، لقد رأينا هذه الروابط، أو هذه المؤشرات،
حرفيًا الأسهم التي تشير إلى انتهاء التنفيذ في التعليمات البرمجية.
فالسهم أو المؤشر هو مجرد عنوان.
لذلك أتعلمون؟
لا يجب أن نطلب من malloc مجرد مساحة كافية فقط للرقم 42،
بدلاً من ذلك، يجب أن نطلب المزيد من الذاكرة في كل هذه المربعات،
لجعلها مستطيلات بشكل تصويري الآن.
لذا الآن، أجل، لدينا هذه الأسهم من الناحية النظرية
والتي تشير من موقع واحد لآخر.
ولكن ما هي القيم التي أريد في الواقع وضعها في هذه المربعات الإضافية الجديدة؟
الجمهور: عناوين القيم التالية.
ديفيد ج. مالان: عناوين القيم التالية.
لذا فهي تشبه فتات الخبز.
إذن في هذا المربع هنا، المرتبط بالقيمة الأولى،
يجب أن يكون عنوان قيمتي الثانية، 475.
المرتبط مع القيمة الثانية هنا، لكل سهم--
ودعوني أرسم السهم من المكان الصحيح.
--من السهم، يجب أن يكون العنوان 150، لأنه الأخير.
ومن ثم، من هذا المربع الإضافي، ما الذي يجب أن أضعه هناك؟
أجل؟
الجمهور: خط مائل 0 أو شيء ما؟
ديفيد ج. مالان: أجل.
لذا من المحتمل، أن معادل خط مائل 0، وهو في عالم استدعاء المؤشر،
فارغ.
لذا هو مجرد قيمة خاصة تعني أن هذا هو كل شيء، هذه هي نهاية السطر.
ما زال يتيح لنا ذلك مجالاً لإضافة قيمة رابعة والإشارة إليها،
ولكنه في الوقت الحالي، يعني لنا بوضوح شديد أنه لا يوجد شيء في الواقع هناك.
إذن ما الذي قمنا بفعله تحديدًا؟
لقد قمنا بإنشاء قائمة القيم 50، أوه عذرًا 42، 50، 13،
لكننا قمنا بربطها سويًا.
أولاً، بشكل تصويري، باستخدام الأسهم فقط.
مثلما قد يقوم به أي شخص باستخدام قطعة من الطباشير.
لكن بشكل فني في التعليمة البرمجية، يمكنني القيام بذلك
من خلال تخزين العناوين فقط في كل من هذه الأماكن.
فقط لنكون واضحين بعد ذلك، ما الذي قد يترجمه هذا بالفعل في التعليمة البرمجية؟
حسنًا، ماذا لو اقترحت هذا.
في التعليمة البرمجية، قد يمكننا القيام بشيء ما مثل هذا.
إذا كنا نريد تخزين عدد صحيح.
فسنحتاج بالطبع إلى تخزين على سبيل المثال العدد الصحيح n، الذي سنقوم بتسميته.
سيقوم n بتمثيل 42، أو 50، أو 13.
لكن إذا رغبنا في إنشاء بنية بيانات،
فقد نرغب في البدء بإعطاء بنية البيانات هذه اسمًا.
لقد قمتُ بتسميتها، منذ لحظة، عُقدة، والتي هي عبارة عن مصطلح في علوم الكمبيوتر لعقدة في
قائمة مرتبطة، إذا جاز التعبير.
وتبدو مثل هذا.
لذا تشير typedef إلى، أعطني النوع الخاص بي.
تشير Struct إلى، اجعله بنية، مثلما كان الطالب.
ثم، العُقدة، والتي ستكون اسمًا لهذا الشيء.
وسأشرح بعد قليل لماذا لدي كلمة عُقدة مرتين هذه المرة.
لكنني تركت مساحة على اللوحة لسطر واحد آخر فقط.
بالإضافة إلى int، التي يُطلق عليه n، أو أيًا كان،
أنا بحاجة إلى تمثيله بطريقة ما في التعليمة البرمجية، الذاكرة الإضافية
التي أريد من malloc أن يعطيني إياها من أجل العنوان.
أولاً وقبل كل شيء، هذه عناوين أي أنواع من البيانات؟
كل هذه المربعات الثلاثة الجديدة.
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: هذه هي عناوين الأعداد الصحيحة في هذه المرحلة من القصة.
لكن من الناحية الفنية، ما الذي يشير إليه هذا المربع بالفعل؟
هل يشير تحديدًا إلى الأعداد الصحيحة؟
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: إنه يشير إلي هذا الجزء الكامل من الذاكرة، اذا صح التعبير.
لذا، إذا بدأت في التفكير بشأن كل هذه المستطيلات على أنها عقدة،
وكل هذه الأسهم على أنها تشير إلى عقدة أخرى،
فنحن بحاجة إلى التعبير عن ذلك بطريقة ما، أنا بحاجة إلى تخزين مؤشر إلى عقدة بطريقة ما.
وبعبارة أخرى، فإن كل هذه الأسهم تحتاج إلى الإشارة إلى عقدة أخرى.
يمكننا قول هذا، في التعليمة البرمجية.
أليس كذلك؟
على سبيل المثال، دعونا نطلق عليها اسمًا.
بدلاً من n، والذي هو عبارة عن رقم، دعونا نسميها next.
إذن next، سيكون اسمًا لهذا الحقل الذي يشير إلى العقدة التالية في الذاكرة.
والعقدة نجمة، ماذا يعني ذلك باللغة الإنجليزية، إذا صح التعبير؟
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: كررها مرة أخرى؟
الجمهور: الإشارة إلى عنوان.
ديفيد ج. مالان: الإشارة إلى عنوان.
أليس كذلك؟
يبدو الأمر مختلفًا.
العقدة هي كلمة جديدة اليوم وهذا جيد.
لكن العقدة نجمة، فقط تعني مؤشر إلى عقدة.
عنوان العقدة.
ومن الواضح أن هذه بنية مخصصة
لذلك يجب أن نقول هذا في الواقع.
لكنه نفس المبدأ على الرغم من أن الأشياء تتصاعد بسرعة
هنا، نحن بحاجة فقط إلى قيم، عدد صحيح، ومن ثم، مؤشر إلى شيء آخر.
سيكون هذا الشيء الآخر عقدة أخرى.
وسنقوم فقط باستخدام عقدة، بصراحة، لتغليف قيمتين--
عدد صحيح ومؤشر.
والطريقة التي تعبر بها في لغة C، وإن كانت مشفرة بطريقة ما،
عن مؤشر، أو أحد هذه الأسهم، هو أنك تقول أعطني متغيرًا يسمى next،
احصل عليه ليشير إلى بنية تسمى عقدة.
أو بالأحرى، احصل عليه ليكون عنوان بنية نوع العقدة.
أجل؟
الجمهور: كيف يمكنك [؟ كشف؟] توقيت struct node [INAUDIBLE]؟؟
ديفيد ج. مالان: سؤال جيد.
إذن هذا يبدو وكأنه نوع من التعريف العام لأنني أقوم بتعريف عقدة،
ومع ذلك، بداخل العقدة عقدة.
هذا جيد بسبب النجمة.
إنها ضرورية في لغة C--
تذكرون أن لغة C دائمًا عبارة عن نوع من القراءة من الأعلى إلى الأسفل.
لذا وفقًا لذلك، هذا السطر الأول من التعليمات البرمجية هنا، typedef struct node،
في هذه المرحلة من القصة، عندما قرأ clang ذلك السطر،
فإنها تعرف أن عبارة struct node، موجودة.
الجمهور: هذا هو السبب في أنك تقول عُقد [INAUDIBLE]..
ديفيد ج. مالان: بالضبط.
بالضبط.
لم نكن بحاجة إلى القيام بذلك مع الطلاب لأنه لم تكن هناك
مؤشرات مرتبطة بالطلاب الآخرين.
لكن ،في هذه الحالة، نعم هناك ترابط.
إذن باختصار، هذا يخبر clang، مرحبًا، clang، أعطني بنية تسمى عقدة.
ومن ثم، هنا، نقول، مرحبًا، clang، كل هذه العقد
يجب أن يكون لديها شيئان، عدد صحيح يسمى n،
ومؤشر إلى نوع آخر من بنيات البيانات تلك من نوع العقدة،
ونسمي الأمر برمته، عقدة.
إنه صعب بعض الشيء.
لكن هذا كل ما في الأمر، كما يلي.
دعوني أمضي قدمًا وأمحي كل هذا.
نوع البيانات هذا بالكامل هو--
إذا تخلصنا من الصورة التي رسمناها بشكل سريع هناك.
--هل هذا يقول، مرحبًا، clang، أعطني بنية بيانات
والتي تبدو كهذا بشكل تصويري.
إنها مقسمة الى جزئين.
الجزء الأول يسمى n، والجزء الثاني يسمى next.
نوع البيانات هذا هو من النوع int.
وهذا مؤشر إلى عقدة أخرى مماثلة.
وهذا كل شيء.
على الرغم من أن التعليمة البرمجية تبدو معقدة، فإن الفكرة هي ذلك بالضبط.
أجل؟
الجمهور: [INAUDIBLE]؟
لماذا يجب عليك قول struct node مرة أخرى؟
ديفيد ج. مالان: سؤال جيد.
السبب هو، كما جاء للتوّ منذ لحظة، clang
ولغة C، بشكل عام، هما أمران سخيفان إلى حد ما.
فهما يقرآن التعليمات البرمجية من الأعلى إلى الأسفل.
والمشكلة هي أنه يجب عليك إعلان اسم هذه البنية
باعتبارها struct node قبل أن تستخدمها في الواقع.
إنها مماثلة في جوهرها لمناقشتنا عن النماذج الأولية-- حيث تحتاج دوال y
إلى أن يتم ذكرها بالأعلى.
هذا فقط يقول لـ clang، أعطني نوعًا يسمى struct node.
أنت لا تعرف كيف ستبدو حتى الآن.
لكن سأنهي فكرتي في وقت لاحق.
ومن ثم هنا، نخبر clang فقط، داخل تلك العقدة
التي يجب أن تكون عددًا صحيحًا، وكذلك، مؤشرًا لنوع الشيء
الذي أنا في طريقي إلى تعريفه.
ولكن إذا قمت بترك كلمة عقدة بالأعلى هناك، وقلنا فقط struct،
فلا يمكنك القيام بذلك لأنها لم ترَ الكلمة N-O-D-E بعد.
هذا كل شيء.
أية أسئلة أخرى؟
حسنًا.
إذا كان لدي الآن بنية بيانات تسمى عقدة،
يمكنني استخدامها لجمع هذه القوائم المرتبطة نوعًا ما معًا.
وربما فقط الأشياء قليلاً،
وللبدء في التخلي عن بعض البط، هل يمكن أن يتطوع
بعض الأفراد لحل مشكلة هنا؟
أجل؟
حسنًا.
تعالوا.
1، 2--
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: بالتأكيد.
أو يمكنك أخذ البطة والذهاب.
حسنًا.
1، 2، ماذا عن 3؟
تعالي هنا، 3.
إذن إذا كنت تريد أن تكون أول مؤشر لدينا، فيمكنك أن تكون رقم 5.
تعال هنا.
تريد أن تكون رقم 9.
وواحد آخر.
متطوع واحد آخر.
تعال هنا.
أجل.
حسنًا.
إذن-- سألتقي بكم هنا.
حسنًا، 17.
حسنًا.
إذن إذا كنت تريد ذلك--
سنقوم باختيار هذا فقط للأشخاص المتابعين من المنزل.
إذا رغبتِ فقط في قول مرحبًا للجمهور.
أندريا: مرحبًا ، أنا أندريا.
[? كومي: ?] مرحبًا، [? أنا كومي. ?]
[? كيونغ: ?] مرحبًا، [? أنا كيونغ. ?]
المتحدث 2: مرحبًا، أنا [INAUDIBLE].
ديفيد ج. مالان: رائع.
حسنًا.
إذا كنتم لا تمانعون في أن ترجعوا جميعًا بمقدار خطوة كبيرة إلى الوراء فوق البط،
فقط حتى نرجع قليلاً إلى الوراء.
دعونا نمضي قدمًا ونقوم بذلك.
إذا كنت أنت أول مؤشر لدينا، لو سمحتم تعالوا إلى هنا على سبيل المثال،
وقفوا فقط خارج منطقة البط.
ولو سمحتم يا رفاق تعالوا إلى هنا قليلاً في المقدمة.
إذن هنا لدينا مقومات قائمة مرتبطة.
ذكريني باسمك مجددًا؟
[? كومي: ?] [? أنا كومي. ?]
ديفيد ج. مالان: [؟ كومي؟] أول مؤشر لدينا إذا صح التعبير.
عبر متغير [؟ كومي؟] إننا فقط
نستمر في تعقب أول عنصر من القائمة المرتبطة.
لذا من فضلك، باستخدام يدك اليسرى، قومي بتمثيل الأول.
فقط أشيري إلى-- ذكريني باسمك مجددًا؟
أندريا: أندريا.
دايفيد ج. مالان: إذن أندريا هي الرقم 9.
من فضلِك استخدمي يدكِ اليسرى للإشارة إلى رقم 5.
من فضلك استخدم يدك اليسرى، أجل، للإشارة إلى رقم 17.
ويدك اليسرى للإشارة فقط إلى فارغ، والذي سنطلق عليه الأرضية.
لذا أنت لا تريد فقط الإشارة إليها بشكل عشوائي
لأن ذلك سيكون مثل اتباع مؤشر وهمي، لذلك هنا يعني فارغ.
حسنًا.
إذن هذه قائمة مرتبطة.
كل ما تحتاجه للتخزين قائمة مرتبطة من ثلاث قيم
هو ثلاث عقد، بداخلها ثلاثة أعداد صحيحة،
وتمثل أيديهم اليسرى الثلاث المؤشر التالي، إذا جاز التعبير.
[? كومي ?] مختلفة قليلاً، حيث إنها لا تحمل قيمة.
إنها لا تحمل عددًا صحيحًا.
وبدلاً من ذلك، تحمل اسم المتغير، الأول.
إذن أنتِ الوحيدة المختلفة هنا بشكل أساسي.
إذن لنفترض أني أريد إدراج الرقم 20.
هل يمكن أن يتطوع شخص ليكون الرقم 20؟
حسنًا.
تعال إلى هنا.
حسنًا.
وما هو اسمك؟
إريك: إريك.
ديفيد ج. مالان: إريك.
إريك، أنت الرقم 20.
وإريك، في الواقع، دعونا نرى.
في الواقع هل يمكننا القيام بذلك؟
دعوني أعطي-- دعوني أجعل هذا الأمر مختلفًا أكثر.
حسنًا.
هذا لم يحدث أبدًا.
حسنًا.
إريك، أعطني ذلك رجاءً.
أرغب في إدراج إريك كرقم 5.
إذن إريك، أنا سأحتفظ بهذه القائمة مرتبة.
إذن إلى أين ستنتقل، بوضوح؟
إريك: أنتقل إلى هناك.
ديفيد ج. مالان: حسنًا.
ولكن قبل أن تفعل ذلك، دعونا فقط نأخذ في عين الاعتبار كيف يبدو هذا في التعليمة البرمجية.
من المفترض، في التعليمة البرمجية، أننا ألغينا تخصيص إريك من الجمهور.
لقد أعطيته قيمة، n من الرقم 5.
ويده اليسرى، إنها قيمة ضئيلة الآن، لأنها لا
تشير إلى أي شيء محدد.
إذن فلديه قيمتان-- عدد صحيح، ويد يسرى تمثل
المؤشر التالي.
إذا كان الهدف هو وضع إريك في ترتيب مُحدد.
فما هي الخطوات التي يجب أن نتبعها؟
على سبيل المثال، يد مَن يجب أن تشير إلى أين، وبأي ترتيب؟
أجل.
أعطنا خطوة واحدة.
الجمهور: يجب أن تشير إلى الرقم 9.
ديفيد ج. مالان: حسنًا، إذن يجب أن تشير إلى الرقم 9،
وهو ما يعاد قول، أشر إلى أي كان أولاً.
إلى أين تشير [؟ كومي؟].
إذن امضِ قدمًا وقم بذلك.
حسنًا، التالي؟
ما هي الخطوة التالية؟
شخص آخر؟
شخص آخر.
كدنا نصل.
أجل؟
الجمهور: يجب أن يشير الأول إلى 5.
ديفيد ج. مالان: حسنًا.
إذن الأول، أو [؟ كومي،؟] هل يمكنك الإشارة إلى 5.
وهذا جيد.
لا يجب عليك التحرك حتى.
أليس كذلك؟
وهذه هي ميزة القائمة المرتبطة.
لا يهم أين أنت في الذاكرة،
هذه هي الميزة الكلية لهذه المؤشرات، حيث يمكنك حرفيًا
الإشارة إلى هذا الموقع الآخر.
انها ليست مصفوفة حيث يحتاجون إلى العودة على التوالي مع بعضهم البعض.
يمكنهم أن يشيروا إلى أي مكان.
حسنًا.
دعونا نمضي قدمًا ونقوم بإدخال واحد آخر.
مَن يريد قول، 55؟
قيمة كبيرة.
أجل.
تعال إلى هنا.
حسنًا.
ما هو اسمك؟
[? كيونغ: ?] [? أنا كيونغ. ?]
ديفيد ج. مالان: [؟ كيونغ. ؟] حسنًا.
إذن تعال إلى هنا.
إذن نحن نقوم فقط بإلغاء تخصيص موقع لـ [؟ كيونغ؟] من الجمهور.
لقد أعطيته قيمته النهائية 55.
يده اليسرى ليست سوى قيمة ضئيلة في الوقت الحالي.
كيف يمكننا إدراج [؟ كيونغ؟] في الترتيب الصحيح؟
إلى أين من المفترض أن ينتقل بشكل واضح؟
في الترتيب المحدد، من الواضح أنه ينتمي إلى النهاية.
ولكن ها هو المغزى من القائمة المرتبطة.
تمامًا كما ناقشنا البحث والترتيب في الماضي،
الكمبيوتر أعمى إلى حد ما عن الكل باستثناء قيمة واحدة فقط.
والقائمة المرتبطة، في الوقت الحالي--
على سبيل المثال، أنا لا أعرف أن هذه الثلاثة، هذه الأربعة، موجودة.
كل ما أعرفه حقًا، هو أن [؟ كومي؟] موجودة.
لأنه عبر هذا المؤشر الأول، المدخل الوحيد
إلى بقية العناصر.
إذن ما هو الرائع في القائمة المرتبطة، لكنه ربما ليس واضحًا،
هو أنك فقط--
القيمة الأكثر أهمية هي القيمة الأولى.
لأنه من القيمة الأولى، يمكنك الوصول إلى أي شخص آخر.
الأمر ليس مفيدًا-- اعذرني، لتذكرها، أندريا؟
--أندريا لوحدها، لأنني إذا قمت بذلك، فلقد فقدتُ فقط
مسار [؟ كومي؟] والأهم من ذلك، بسبب رقمه،
إريك.
لذلك كل ما يجب القيام به حقًا، هو تذكر [؟ كومي. ؟]
إذن إذا كان الهدف الآن هو إدخال الرقم 55، فما هي الخطوات التي يجب أن تأتي أولاً؟
لا أقصد التورية.
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: كررها مرة أخرى.
الجمهور: العثور على المساحة الأولى.
ديفيد ج. مالان: حسنًا.
العثور على المساحة الأولى.
إذن سأبدأ [؟ بكومي،؟] وسأتبع هذا المؤشر.
رقم 5، هل ينتمي رقم 55 إلى هنا؟
لا.
إذن سأتبع هذا المؤشر وأنتقل إلى أندريا.
هل ينتمي 55 إلى هنا؟
لا.
سأتبع مؤشرها، و22، هل ينتمي إلى هنا؟
لا.
سأتبع هذا المؤشر، 26؟
لا.
لكن يدك حرّة، كما يتضح الأمر.
إذن ما هي الخطوة التالية؟
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: يمكننا جعله يشير إلى 55، والآن انتهينا.
إذن هذا أمر بسيط نسبيًا، ولكن ما هو وقت تشغيل هذا؟
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: إنه حرف O كبير من n.
إنه خطي.
لأنه كان يجب أن أبدأ أولاً، حتى لو
توفر لدينا نحن البشر رفاهية استراق النظر إليها فقط.
أن أقول، أوه، من الواضح، أنه ينتمي في طريقه إلى النهاية.
امم-امم.
ليس في التعليمة البرمجية.
مثلما، يجب أن نبدأ في البداية بعكس القائمة اللعينة بالكامل،
حتى نصل بشكل خطي إلى النهاية.
والآن ها قد انتهينا.
دعونا نجرب شيئًا أخيرًا.
ماذا عن 20؟
أجل.
عظيم.
تعال إلى هنا.
ما هو اسمك؟
جيمس: أنا جيمس.
ديفيد ج. مالان: جيمس.
حسنًا، جيمس.
حسنًا.
إذن ألغينا تخصيص جيمس، ومنحناه الرقم 20.
من الواضح تقريبًا أنه ينتمي إلى المنتصف.
ما هي الخطوة الأولى؟
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: معذرةً؟
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: حسنًا.
إذن نبدأ مع [؟ كومي،؟] مجددًا.
حسنًا.
أولاً، حسنًا.
5، هل ينتمي إلى هنا؟
لا.
دعوني أتبع الرابط.
حسنًا 9، هل ينتمي إلى هنا؟
لا.
هل تنتمي إلى 22-- أوه.
ولكن ما الخطأ الذي ارتكبتُه للتو؟
لقد ذهبتُ بعيدًا للغاية.
على الأقل في هذا الأمر.
مثل، أنا حرفيًا-- أندريا خلفي الآن.
حسنًا.
هل يمكنني أن أتبع المؤشر من الخلف؟
لا يمكنك.
على سبيل المثال في كل صورة قمنا برسمها، وفي كل مثال
قمنا به باستخدام عنوان، لدينا فقط عنوان مؤشر next.
ليس لدينا ما يسمى، بالقائمة المرتبطة بشكل مضاعف، على الأقل في هذا الأمر،
حيث يمكنني فقط الالتفاف.
إذن كان هذا خطأ.
لذا أحتاج إلى البدء من جديد بدلاً من ذلك.
أولاً، حسنًا 5، حسنًا 19--
ما أحتاجه حقًا في التعليمة البرمجية، في نهاية المطاف، هو
إلقاء نظرة خاطفة إلى الأمام وليس التحرك بالفعل-- بعيدًا هكذا.
تحديدًا نحو 22.
سألقي نظرة خاطفة إلى الأمام على 22، وأدرك، أوه، سيكون هذا بعيدًا للغاية.
هذا ليس بعيدًا بما فيه الكفاية.
لذلك دعونا نمضي قدمًا ونحضر جيمس إلى هنا.
حسنًا، في الواقع، يمكنك البقاء هناك من الناحية المادية.
لكن ما هي الخطوة التي يجب أن تحدث أولاً؟
أعرف الآن أنه ينتمي إلى هنا.
هل تريدين أن تشيري إليه؟
حسنًا.
أشيري إليه.
أندريا: أوه.
معذرة، يشير هو أولاً.
ديفيد ج. مالان: حسنًا، دعونا نفعل ذلك، فقط لأنه غير صحيح.
هذا جيد.
حسنًا.
اقترحت أندريا أن نشير إلى هنا، ولكنها حطمت للتو القائمة المترابطة بالكامل.
لماذا؟
أندريا: لأنه لا يوجد شيء نشير إليه.
ديفيد ج. مالان: صحيح.
لا أحد يتذكر-- ما هو اسمك مجددًا؟
[? كيونغ: ?] [? كيونغ. ?]
ديفيد ج. مالان: لا أحد يتذكر أين كان [؟ كيونغ ؟].
لذا لا يمكنك فعل ذلك.
يجب أن تظل يدك اليسرى تشير إلى هناك.
إذن ما هي الخطوات التي يجب أن تحدث أولاً بدلاً من ذلك؟
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: يجب على جيمس أن يشير إلى أي شيء
تشير إليه أندريا، من المحتمل؟
لذا يوجد تكرار بعض الشيء في هذه اللحظة، مثلما كان الأمر من قبل.
حسنًا.
الآن ما الذي يحدث بعد ذلك؟
هذه هي الخطوة الأولى.
أندريا: الآن يمكنني أن أشير.
ديفيد ج. مالان: الآن يمكنك أن تشيري إليه.
حسنًا.
يمكنك القيام بذلك.
حسنًا.
والآن، يبدو هذا فوضويًا بالكامل،
لكن إذا عرفنا أن [؟ كومي ؟] هي الأولى،
يمكننا أن نتبع الآثار إلى إريك، ثم إلى أندريا، ثم إلى جيمس،
ثم بقية قائمتنا خطوة تلو الأخرى.
إذن إنها كمية كبيرة من الدالة المنطقية الآن.
لكن ما المشكلة التي قمنا بحلها؟
وأعتقد أننا حددنا ذلك هنا في وقت سابق.
ما هي المشكلة أولاً وقبل كل شيء بالمصفوفات؟
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: يجب أن تقرر حجمهم مسبقًا.
وبمجرد قيامك بذلك، إذا أردت إضافة عنصر إضافي،
فيجب عليك تغيير حجم الشيء كله.
والذي يُعد مكلفًا لأنك يجب أن تحرك الجميع.
الآن وبصراحة، سأصبح طماعًا قليلاً هنا.
وفي كل مرة نقوم بإدخال هذه العناصر الجديدة،
كنت أُبقيها في ترتيب محدد.
لذا يبدو أنك إذا أدرجتَ الأشياء بترتيب مُحدد، حدث حرف o كبير،
كل مرة.
لأنه في أسوأ الحالات، قد ينتهي العنصر الجديد
بالتأكيد في النهاية.
ولكن ماذا لو خفّفنا هذا القيد؟
ماذا لو لم أكن متوترًا للغاية وأحتاج إلى أن يكون كل شيء لطيفًا ومنظمًا ومرتبًا؟
ماذا لو أردتُ فقط الاستمرار في زيادة القائمة بأي ترتيب عشوائي؟
وقمتُ بتخصيص الرقم 34.
وسأشغّل الرقم 34.
Malloc 34.
ما هو أسرع مكان لأنتقل إليه؟
أجل؟
الجمهور: أشر إلى 5، ثم احصل على [INAUDIBLE]..
ديفيد ج. مالان: حسنًا.
سأشير إلى 5، ثم، [؟ كومي، ؟] إذا أمكنك الإشارة إليّ.
تم.
خطوة-- حسنًا، خطوتان.
حسنًا.
لنفترض الآن، أنني أخصص جزءًا من الذاكرة لـ 17 مع شخصٍ آخر، والذي سنتظاهر
بأنه هنا مباشرةً.
ما هو أفضل مكان للرقم 17 للانتقال إليه؟
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: مباشرةً بعد [? كومي ?] أيضًا.
لذا الآن، [? كومي ?] يمكن أن تشير إلى 17، 17 يمكن أن يشير إليّ، ويمكنني أن أشير إلى إريك،
وهكذا.
وهاتان الخطوتان مرة أخرى.
خطوتان-- إذا كان نفس عدد الخطوات في كل مرة،
سنطلق على ذلك، الوقت الثابت.
ونكتبه كحرف o كبير من 1.
وهنا أيضًا، إنها مجرد مقايضة.
إذا كنت تريد إدراجات سريعة بالفعل، لا تقلق بشأن الترتيب.
ضَعهم فقط في البداية وتعامل معهم لاحقًا.
إذا كنت تريد إمكانية تغيير حجم ديناميكية لا تستخدم مصفوفة، استخدم قائمة مرتبطة،
وفقط حافظ على التخصيص أكثر وأكثر حيثما تنتقل دون إهدار
كمية كبيرة من المساحة أيضًا.
لاحظوا أن، هذه مشكلة كبيرة أخرى بالمصفوفة.
إذا كنت تخصص مساحة أكثر من اللازم، وتستخدم فقط جزءًا منها، فأنت تهدر مساحة فقط.
لذا لا يوجد حل واحد هنا.
ولكن الآن لدينا القدرات، والفضل يعود إلى البنيات
والمؤشرات لجمع هذه المشكلات الجديدة معًا، إذا أردتَ.
نعم، من فضلك.
المتحدث 2: لماذا لا تستطيع العقدة [INAUDIBLE]؟؟
ديفيد ج. مالان: ومن أنا في هذه القصة؟
المتحدث 2: [INAUDIBLE].
ديفيد ج. مالان: أوه، حسنًا.
قطعًا.
لذا فكرة أخرى معقولة جدًا ستكون، حسنًا،
لماذا لا نضع تلك الجديدة في النهاية؟
هذا جيد إذا كنت أقوم بمتابعة مسار مَن هو في النهاية.
المشكلة، هي في هذه اللحظة في القصة،
وسنرى في النهاية هذا في التعليمة البرمجية، أتذكر فقط [؟ كومي. ؟]
ومن [? كومي ?] سأحصل على أي مكان آخر.
يمكنني الحصول على مؤشر آخر، مؤشر ثانٍ،
وأطلق عليه حرفيًا، الأخير، هذا يعادلك.
أو يشير هذا دائمًا إليك.
أنا فقط بحاجة إلى مؤشرين، أحدهما يسمى حرفيًا الأول،
وواحد يسمى حرفيًا الثاني.
هذا جيد.
هذا تحسين جيد إذا أردت إلقاء كل العناصر في النهاية.
وبصراحة، يمكنني أن أكون خياليًا حقًا--
ولحل المشكلة التي ذكرتها أندريا في وقت سابق--
إذا قمت بتخزين ليس فقط عدد صحيح ومؤشر، ولكن بدلاً من ذلك،
عدد صحيح ومؤشرين، يمكنني جعل كل شخص
من هؤلاء الشباب أن يشيروا بأيديهم اليسرى واليمنى
في قائمة مرتبطة بشكل مضاعف، وذلك لحل المشكلة التي ذكرتها أندريا، والتي
كانت إذا ذهبت بعيدًا للغاية فليس هذا بالأمر المهم.
ارجع خطوة للخلف.
ليس علي أن أفكر بجد حول هذا المنطق.
لذا هناك أيضًا، مقايضة.
دعونا نمضي قدمًا ونأخذ خمس دقائق استراحة.
سأشغل بعض الموسيقى.
التقط بطة الآن، إذا كنت ترغب في ذلك.
وسنعود مع بعض بنيات البيانات الأكثر خيالاً إلى الآن.
شكرًا.
حسنًا.
عدنا.
لذلك دعونا الآن نترجم بعض هذه الأفكار إلى تعليمة برمجية.
بحيث يمكننا في الواقع حل هذه المشكلة بشكل
ملموس أكثر من مجرد وجود بشر يشيرون إلى بعضهم البعض.
لذا على سبيل المثال، دعونا نحاول استخلاص كل شيء
كنا نتحدث عنه في مجرد هدف في التعليمة البرمجية
الخاصة بتخزين قائمة من الأرقام.
أقترح أنه يمكننا أخذ ثلاث بطاقات في هذه المسألة.
ستكون الأولى، دعونا فقط نقرر مسبقًا كم عدد الأرقام التي
نريد تخزينها بحيث لا نضطر إلى التعامل مع كل
هذا التعقيد في الإشارة والمؤشرات وكل هذا،
وفقط التعليمة البرمجية الثابتة والتي لها قيمة بطريقة ما، ونتوقف فقط
عندما يُدخل المستخدم العديد من الأرقام وليس أكثر من ذلك.
الثانية، يمكننا تحسين ذلك وعلى الأقل ندع المستخدم يقوم بتغيير حجم
مصفوفته بشكل ديناميكي.
لذا إذا قرر إدخال أرقام أكثر مما كنا نتطلع إليه،
ستنمو وتتعامل مع هذا.
بالطبع، المصفوفات ليست بالضرورة مثالية
لأنها يجب عليها أن تقوم بكل هذا النسخ من القديم إلى الجديد.
هذا زمن خطي.
سيبدو أكثر ذكاء للحصول على subversion 3، الذي
سيستخدم في الواقع قائمة مرتبطة.
إذن نحن فقط بكل تواضع نقوم بتخصيص مساحة لرقم آخر،
ورقم آخر، ورقم آخر، أو عقدة بالفعل.
رقم واحد في كل مرة.
لذا دعوني أمضي قدمًا وأبدأ على النحو التالي.
سأمضي قدمًا وسأضمّن بعض السطور المألوفة في قائمة 0.c،
من مكتبة CS50، فقط لجعل الأمر سهلاً للحصول على بعض مدخلات المستخدم لهذا الغرض.
وstandard iO نقطة h، لأجل printdef.
ودعوني أمضي قدمًا وأعلن عن الدالة الرئيسية الخاصة بي كالمعتاد.
وبعد ذلك، هنا دعونا نقوم ببعض الأشياء.
أولاً، دعونا نطلب من المستخدم سعة المصفوفة
التي سنستخدمها.
أو بالأحرى، دعونا نقوم بذلك أولاً.
دعوني أولاً أرجع وأقول، أتعرفون ما الأمر؟
عدد صحيح، أرقام، 50.
حسنًا، سيكون هذا الأمر مزعجًا للكتابة في 50 رقمًا.
سنعطي المستخدم رقمين في البداية، وهي هنا، يمكنها الكتابة فيها.
بعد ذلك، دعونا نمضي قدمًا ونطلب من المستخدم لهذه الأرقام.
لذا دعوني أمضي قُدمًا وأقول--
لنقم بذلك.
دعونا على الأقل نقوم بتنظيف هذا قليلاً حتى نتمكن من إعادة استخدام هذه القيمة.
إذن ليس لدينا رقم سحري.
ظهر هذا للتو في المناقشة في الواقع.
لذا بينما-- هل أريد القيام بذلك؟
كلا.
دعوني أصلح هذا.
ستكون هذه سعتي من الحجم 2.
وهذا سيعطيني هذا الحجم.
وبعد ذلك، سأستمر في تعقب كم عدد الأعداد الصحيحة
التي طلبتها من المستخدم حتى الآن.
لذا في البداية، سيكون حجم هذه البنية 0.
لكن ستكون سعتها، إذا جاز التعبير، 2.
لذا الحجم يشير إلى عدد الأشياء الموجودة بها.
تشير السعة إلى عدد الأشياء التي يمكن أن تكون بها.
وفي نفس الوقت حجم البنية أقل من سعتها،
لنمضِ قدمًا ونحصل على بعض المدخلات من المستخدم.
دعونا نمضي قدمًا ونطلب منه رقمًا، وذلك باستخدام صديقنا القديم، get int.
ولنقل فقط، أعطني رقمًا.
ثم، دعوني أمضي قدمًا وأدخل الرقم
الذي كتبه في هذه المصفوفة في حجم موقع، هكذا.
وبعد ذلك، أكتب حجم زائد، زائد.
أعتقد.
لقد كتبته بسرعة.
لكن لنفكر في ما فعلته للتو.
قمت بتهيئة الحجم إلى 0، لأنه لا يوجد شيء فيه من البداية.
ثم أقول، بينما يكون الحجم أقل من سعة كل شيء--
والسعة هي 2 افتراضيًا--
فامضِ قدمًا وقم بما يلي.
أعطني عددًا صحيحًا من المستخدم.
حسنًا.
إذن int number = get-int.
ثم ضَع في الموقع، الحجم، في أرقامي، مصفوفة،
أيًا كان ما كتبه الشخص فيها، فهو رقم.
ثم، قم بزيادة الحجم باستخدام زائد، زائد.
حسنًا.
لذا في التكرار الأول الحجم يساوي 0.
لذا الأرقام، قوس، 0، تحصل على الرقم الأول.
الأرقام، قوس، 1، تحصل على الرقم الثاني.
ثم، الحجم يساوي السعة.
لذلك يتوقف، منطقيًا.
أي أسئلة حول منطق هذه التعليمة البرمجية؟
حسنًا.
لذا بمجرد حصولنا على هذه الأرقام، فلنقم فقط بشيء بسيط.
على سبيل المثال، for int, i get 0.
I أقل من الحجم الفعلي I، زائد، زائد.
دعونا فقط نمضي قدمًا ونطبع الرقم
الذي أدخلته، %i، خط مائل عكسي n، ونكتب الأرقام، قوس، i.
حسنًا.
لذلك إذا لم أقم بأية أخطاء في القائمة 0 نقطة C، إذن، سأمضي قدمًا
وأكتب نقطة، خط مائل، o، نقطة، C. وسيطلب مني كتابة رقمين.
لنمضِ قدمًا ونقوم بكتابة 1، 2.
قمت بإدخال 1، وقمت بإدخال 2.
حسنًا.
ليس سيئًا.
ولكن هذا تصميم سيء، بالتأكيد، لماذا؟
فقط اعثروا على خطأ واحد. إنه صحيح.
ولكن التصميم سيئ.
الجمهور: متكرر.
ديفيد ج. مالان: متكرر، لأنني أستخدم زوجًا من التكرارات الحلقية، بالتأكيد.
وهو في الأساس-- محدود للغاية في الدالة.
لماذا؟
ما مدى فائدة هذا البرنامج؟
الجمهور: تم ترميزه بشكل ثابت في 2.
ديفيد ج. مالان: أجل.
تم ترميزه بشكل ثابت في 2.
لذا دعونا على الأقل نقوم بتحسين هذا قليلاً،
والتخلص من هذه التعليمة البرمجية الثابتة.
لماذا لا أطلب على الأقل من المستخدم شيئًا كهذا؟
حسنًا، بدلاً من مجرد الإعلان عن السعة، دعوني أمضي قدمًا وأقول،
أتعلمون ماذا؟
دعونا فقط نستبدل 2.
get int، ولنقل فقط السعة، على سبيل المثال.
حسنًا.
والآن إذا قمت بهذا، سيُطلب مني--
إذن make list0.
نقطة خط مائل list 0.
ستكون السعة 2.
1، 2، هذا لطيف.
ولكن إذا قمت بتشغيله مرة أخرى، وأعطيته السعة 3--
1، 2، 3، أحصل على المزيد من السعة.
لذا هذا لطيف.
إنه تحسن بالتأكيد.
يوجد خطأ هنا.
قبل أن أخضعه لمزيد من الاختبارات، هل يمكن لأحد أن يُعرّف الخطأ أو بطريقة ما يقوم بتعطيل هذا؟
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: أوه، امضِ قدمًا.
الجمهور: إذا لم تقم بإدخال عدد صحيح.
ديفيد ج. مالان: إذا لم أقم بإدخال رقم صحيح.
أو-- هل هو نفس التعليق بالأعلى هنا؟
الجمهور: سأقول، ماذا يحدث إذا رجعت للوراء
ووضعت في [INAUDIBLE] تلك [INAUDIBLE] الأخرى التي ستكون في الذاكرة.
ديفيد ج. مالان: أوه.
لا.
لأنني أعيد تشغيله في كل مرة.
لا أحتاج إلى القلق بشأن عمليات إعادة التشغيل السابقة للبرنامج.
أجل؟
الجمهور: في التكرار الحلقي من نوع for، إنه فقط 1،
2، 3، إنه لا يهتم في الواقع كيف وضعتَه.
ديفيد ج. مالان: [INAUDIBLE] 1، 2، 3-- حسنًا، أنا أتحدث عن الحجم،
والذي يمكن أن يكون السعة.
لأنهما في النهاية الآن متعادلان.
لأنني أقوم بتعبئة كل شيء.
لكن دعونا نجرب ذلك.
إذا لم تكتب قيمة.
لذا دعوني أمضي قُدمًا وأعيد تشغيل هذا.
ستكون سعتي duck.
حسنًا.
لذا تعاملنا مع ذلك.
لأن getInt قام بذلك لأجلي.
لكنني أراهن أنه ما يزال بإمكاني تعطيل هذا.
أوه، نعم، دعونا دائمَا نحاول تجربة شيء سلبي.
أوه، حسنًا.
سيء جدًا.
مثل الرسالة التي تبدو مشفرة، ولكن بوضوح،
لها علاقة بقيمة سالبة.
لذا ربما يجب أن أكون أكثر ذكاء قليلاً بشأن هذا الأمر.
وتذكرون أننا من الأسبوع 1، قمنا بذلك.
مع ماريو، قد قمت بذلك.
حتى أتمكن من القيام بشيء على سبيل المثال، القيام، عندما تكون السعة أقل من 1.
يمكنني أن أمضي قدمًا وأقول، السعة getInt السعة.
لذا التحقق من الخطأ إلى حد ما لإغلاق الخطأ الذي قمت بتحديده.
حسنًا.
لذا لنمضي قُدمًا ونعد تحويل هذا برمجيًا.
Make lists 0-- عذرًا سنبدأ بسماع هذا كثيرًا اليوم.
ألم [INAUDIBLE]؟
Make list0، نقطة، خط مائل، list0.
ستكون السعة 3.
1، 2، 3.
ستكون السعة الآن -1.
لا تسمح بذلك.
السعة 0، لا تسمح بذلك.
السعة 1، نعم.
لذا بشكل غير شامل، لقد اختبرت ذلك.
يبدو الأمر كما لو كان في شكل أفضل.
حسنًا.
لكن هذا البرنامج، في حين أنه صحيح، وبه ميزات أكثر،
ما يزال لديه هذا الحد الأساسي.
ألن يكون من الجيد السماح للمستخدم بمواصلة كتابة الأرقام فقط،
بقدر ما يريد، ثم الخروج بمجرد الانتهاء من إدخال الأرقام.
أليس كذلك؟
إذا كنت بصدد إنشاء برنامج لبرمجة GPA الخاص بشخصٍ ما،
قد يشترك طلاب مختلفون في دورات مختلفة،
أنت لا تريد أن يسجل جميعهم في الدورات 32 كلها.
إذا كانوا أصغر سنًا ولم يشتركوا في جميع هذه الدورات.
على سبيل المثال توجد العديد من السيناريوهات إذا كنت لا تعرف مسبقًا عدد
الأرقام التي يريد المستخدم تقديمها.
لكنك تريد دعم بضعة أرقام، الكثير من الأرقام، أو ما يفوق ذلك.
لذلك لنقم بذلك في إصدار ثانٍ.
في list 1 نقطة C، دعوني أمضي قدمًا وأقوم بتحسين هذا المثال على النحو التالي.
أولاً، دعوني أعطي أصدقائي المعروفين هنا CS50 نقطة لمعيار IO،
standard iO نقطة h، ثم، هنا، int main void.
ومن ثم، دعونا نبدأ في كتابة هذا.
لذا الآن، لا أعرف مسبقًا، بالضرورة، ما عدد الأرقام
الذي سيكتبه المستخدم.
على سبيل المثال الهدف هو، أنني أريدهم قادرين على كتابة
رقم، ورقم آخر، ورقم آخر، ومن ثم
أضرب المعادل من هذا، q، للخروج، عند الانتهاء من إدخال الأرقام.
كما أنني لا أريدهم أن يفكروا مسبقًا، ما هو عدد الأرقام
الذي يدخلوه.
لكن كيف يمكنني فعل ذلك؟
كما لو أنه لا يمكنني فقط إنشاء مصفوفة تُسمى أرقام، وقول، 50.
لأنه إذا أراد المستخدم كتابة 51 رقمًا،
سأضطر إلى تغيير حجم ذلك.
لكن كيف يمكنك تغيير حجم مصفوفة؟
كيف تقوم بتغيير حجم مصفوفة؟
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: ما هذا؟
الجمهور: لا يمكنك ذلك.
ديفيد ج. مالان: لا يمكنك ذلك.
حسنًا.
لم نر مثالاً حيث تم تغيير حجم مصفوفة من قبل.
تحدثنا عن ذلك على اللوح هنا.
حسنًا، فقطمثل، تخصيص واحدة أكبر ونسخ كل شيء فيها.
وقد حددنا realloc.
لكن لا يمكنك في الواقع استخدام realloc على مصفوفة.
يقبل realloc بالفعل عنوان جزء من الذاكرة
الذي تريد تكبيره، أو تقليصه.
لذا يتضح أنه، إذا بدأنا الآن في استغلال
هذا النوع من التعريف الأساسي لما تعنيه المصفوفة، جزء من الذاكرة،
يمكننا في الواقع بناء المصفوفات بأنفسنا.
إذا كانت المصفوفة فقط جزءًا من الذاكرة، أو بشكل أكثر تحديدًا،
إنها مثل عنوان وحدة البايت الأولى من جزء من الذاكرة،
يبدو أنني سأتمكن من إعلان مصفوفتي، وليس باستخدام أقواس مربعة
كما كنا نفعل لمدة أسابيع، ولكن يمكنني القول،
أنتم تعرفون ما هي الأرقام حقًا، إنها مجرد مؤشر فقط.
وأنا في البداية سأقوم بتهيئتها إلى فارغ.
لأنه لا توجد مصفوفة.
لكن الآن لدي القدرة للإشارة إلى هذا المؤشر
في أي جزء من الذاكرة، صغيرًا كان أو كبير.
الآن لماذا هذا مفيد؟
حسنًا دعوني في البداية أزعم أن سعتي هي 0،
لأن لا شيء يحدث حتى الآن.
أنا لم أطلب malloc أو أي شيء.
وفي البداية، يكون حجمي هو 0 لأنه لا يوجد شيء في المصفوفة.
وليس لديها حجم.
لكن دعوني أفعل هذا إلى الأبد.
تمامًا كما كان في سكراتش، لدينا كتلة أبدية يمكنك استخدامها، عندما تكون صحيحة، وC،
لأقول فقط استمر في فعل هذا حتى يعطل المستخدم هذا.
ودعوني أمضي قدمًا وأطلب من المستخدم، أعطني رقمًا، getInt.
وأطلب منه رقم فقط.
ثم، نحتاج فقط إلى مكان لوضع ذلك فيه.
إذن أين أضع هذا الرقم؟
حسنًا، هل لدي، في هذه اللحظة، أي مكان لوضع الرقم فيه؟
لا.
ومن الناحية الفنية، كيف يمكنك التعبير عن ذلك؟
كما هو الحال في رمز زائف، أريد أن أقول، إذا لم يكن هناك مكان لرقم.
لكن من الناحية الفنية، يمكنني القيام بذلك.
حسنًا، إذا كان حجم المصفوفة في هذه اللحظة، يساوي سعتها،
هذا يبدو وكأنه وسيلة ذات مستوى أقل للتعبير عن نفس الشيء.
أيَا كانت السعة، إذا كان الحجم هو نفسه، لا توجد مساحة إضافية.
كما تخفي هذه العبارة البسيطة أيضًا السيناريو حيت تكون السعة 0،
وبالتالي الحجم 0.
لذا إنه نفس السؤال.
سواء لم تكن لدينا مساحة للكل، أو كانت لدينا بعض المساحة
ولكننا استخدمناها كلها-- الحجم يساوي، يساوي، السعة.
لذا إذا كان الحجم يساوي السعة، أو أضعه بشكل مباشر أكثر،
إذا لم تكن لدي مساحة كافية.
فما الذي أرغب في فعله بديهيًا؟
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: تخصيص المزيد من الذاكرة.
ويتضح أنه، كما اقترحت، أو كما اقترح شخص ما في وقت سابق،
إعادة تخصيص الذاكرة.
يمكننا استخدام هذه الدالة لأول مرة.
دعوني أمضي قدمًا وأقول ذلك--
الجانب السلبي لاستخدام realloc هو أنه يجب عليك أن تكون ذكيًا حول هذا الأمر،
لأنه يعيد مؤشر.
لذا دعوني أقترح هذه التعليمات البرمجية أولاً.
أولاً، فقط أعطني متغير مؤقت، أطلق عليه، temp،
سيُخزِّن ما يلي.
في الواقع، لا.
دعوني أبدأ هذا ببساطة أكثر.
دعوني أمضي قدمًا وأقول، إنه يجب إعادة تخصيص الأرقام من فضلك،
realloc عن طريق تمريرها.
وهذه المرة، أعطني حجم عدد صحيح، المرات--
كم عدد الأعداد الصحيحة التي أريدها هذه المرة؟
كم عدد الأرقام التي يدخلها الإنسان افتراضيًا فقط؟
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: واحد فقط.
حسنًا.
لأنه حرفيًا، نحن نستدعي getInt مرة واحدة في هذه القصة.
لذا أيًا كان حجم هذه المصفوفة الآن، نحتاج إلى زيادتها بمقدار 1.
هذا كل شيء.
إذن هذا السطر من التعليمات البرمجية هنا يقول، مرحبًا جهاز الكمبيوتر،
أمضي قدمًا وأعد تخصيص هذه المصفوفة من أيًا كان حجمها الحالي،
واجعلها بهذا الحجم بدلاً من ذلك.
حجم أيًا كان، زائد 1، مرات حجم عدد صحيح.
لأن هذا هو ما نحاول تخزينه، وهو عدد صحيح.
لذلك علينا القيام بهذا الضرب.
وrealloc، كما ذكرنا في وقت سابق، أداة رائعة.
ستأخذ مؤشر، أيًا كان جزء الذاكرة
الذي قمت بإعادة تخصيصها بالفعل، ثم ستقوم بعد ذلك بإعادة تخصيص
جزء أكبر من الذاكرة.
آمل، أن ما سيحدث هو هذا--
إذا بدا جزء الذاكرة الخاص بك في البداية هكذا،
آمل أن نلاحظ، أوه، تم تحرير هذه الذاكرة.
دعوني فقط أعود بكم إلى نفس العنوان.
لذلك إذا كان هذا هو العنوان 100، وكنت محظوظًا
وهذا العنوان أيضًا متاح، وستتذكر دالة realloc
ذلك لنظام التشغيل.
ستعيد الرقم 100 مرة أخرى.
والانتقال أمر جيد.
يمكنك لمس الذاكرة بأمان هنا.
أو إذا كان هذا قيد الاستخدام بالفعل، هذا الجزء من الذاكرة، وبالتالي لا
لا يمكننا ملائمة وحدة بايت آخرى هنا لأن بعض التعليمات البرمجية الأخرى التي كتبتها
تستخدم تلك الذاكرة.
ولكن هناك ضعف الذاكرة المتوفرة هنا.
ما سيفعله realloc، هو إذا قمت بتخزين الرقم 50،
سيعالج عملية نسخ 50 إلى القيمة الجديدة.
سيُترك هذا كقيمة ضئيلة لك لتعالجها.
وسيعيد إليك عنوان الجزء الجديد من الذاكرة،
بعد أن قام بالنسخ لأجلك.
لذلك على الرغم من أنه من الناحية الفنية إعادة تخصيص المصفوفة،
لن يقوم حتمًا بتكبيرها فقط.
قد يقوم بإعادة تخصيصها في الذاكرة إلى جزء أكبر،
ثم يعطيك العنوان الجديد لتلك الذاكرة.
أي سؤال؟
الجمهور: هل هذه العملية هي مفضلة حقًا
لإتشاء ذاكرة إضافية في مكانها فقط.
ومن ثم توفير الوقت والطاقة في إعادة تخصيصها [؟ جميعًا مرة واحدة. ؟]
ديفيد ج. مالان: هذا سؤال جيد حقًا.
بصراحة، يمكننا تجنب هذه المشكلة قليلاً عن طريق القيام بذلك، أتعرفون ماذا،
أعطني على الأقل--
أمضي قدمًا وأعطني على الأقل حجم عدد صحيح، مرات--
أنا لا أعرف، لن يكتب معظم البشر أكثر من 50 رقمًا.
دعونا نختار فقط 50.
لذا يمكنك القيام بذلك، وهذا بالفعل سيوفر لك الوقت.
لأن النهج الذي أتبعه حاليًا
غير فعّال إلى حد ما لأن كل مرة يستدعي المستخدم
getInt، ويعطي عدد صحيح، نحن نقوم بتغيير الحجم، بتغيير الحجم، بتغيير الحجم.
مكلّف للغاية.
بالنسبة إلى أفضل قيمة هي على الرغم من ذلك--
50؟
هل يجب أن تكون 25؟
هل يجب أن تكون 1000؟
سأقوم بتقليل الرهان أو زيادته.
ويعتمد الأمر عليك لتقرير أيًا من هذه الطرق هو القرار الأسوأ.
الجمهور: ولكن، من حيث البرامج،
إنه مكلف إلى حد ما أيضًا للحصول على ذاكرة لا تستخدمها
أو بشكل عام، هل هو عادة أكثر من جيد؟
ديفيد ج. مالان: سؤال جيد.
في البرامج التي تكتبها، هل من الأفضل الحصول على المزيد من الذاكرة أكثر من الذي تستخدمه،
أو يجب أن تكون معتدلاً بالفعل؟
هذه الأيام، الذاكرة رخيصة.
نمتلك جميعًا وحدات جيجابايت من الذاكرة.
لذا إهدار 50 وحدة بايت أو 200 وحدة بايت، 4 أضعاف، من الذاكرة، ليس بالإمر الهام.
مجرد القيام بالوظيفة بسرعة وسهولة.
لكن في الأجهزة محدودة الموارد، ربما، الأشياء مثل الهواتف
أو أجهزة المصممة بتقنية إنترنت الأشياء قليلاً
والتي لديها موارد أقل، أنت لا تريد حقًا إهدار وحدات بايت.
ولكن بصراحة، وحدات المعالجة المركزية (CPU)، الأدمغة الموجودة في أجهزة الكمبيوتر الخاصة بنا،
سريعة جدًا هذه الأيام، حتى لو قمت باستدعاء malloc
10 مرات، 1000 مرة، يحدث ذلك بسرعة
لا يلاحظها الإنسان حتى.
لذا هناك أيضًا.
هذه ما يُطلق عليها قرارات التصميم.
وهذه أنواع الأشياء التي، في العالم الحقيقي،
قد تناقشها مع شخص ما في الواقع على لوحة بيضاء،
قائلاً، لا، هذا غباء لهذا السبب.
أو ربما يدفعك أو تدفعك للخلف لأسباب أخرى.
وليس بالضرورة أن يكون أي منكما على صواب.
الهدف وراء هذا كله هو عملية التفكير أولاً
حتى تكون على الأقل واثقًا في اختيارك.
أجل؟
الجمهور: عندما نكتب في ملف في PSET الأخيرة،
هل تقوم بتخزينها في الذاكرة أولاً أو تضعها مباشرة على محرك الأقراص الصلبة؟
ديفيد ج. مالان: عندما تستدعي fread،
فأنت تقرأ من خلال تعريف مجموعة مشاكل الأدلة الجنائية
وحدات البايت من القرص في الذاكرة.
عندما تدعو fwrite، فأنت تقوم بنسخ وحدات البايت من الذاكرة إلى القرص.
إذا كان هذا يجيب على السؤال.
حسنًا.
أي أسئلة أخرى؟
أجل؟
الجمهور: لماذا قلت، حجم + 1، في السطر 16؟
ديفيد ج. مالان: لماذا قلت، الحجم + 1، في السطر 16؟
لأن الهدف كله هو تخصيص مساحة في هذه المصفوفة للرقم الذي تم إدخاله حديثًا
والذي كتبه الإنسان للتو.
وبغض النظر عن الحجم الحالي للمصفوفة،
من الواضح أنني بحاجة إلى مساحة أخرى.
الجمهور: هذا إذن يتكرر مرارًا وتكرارًا؟
ديفيد ج. مالان: إنه يتكرر مرارًا وتكرارًا.
لأنه في هذه اللحظة، أنا داخل هذا أثناء التكرار الحلقي.
لذلك نحن بحاجة لطرح سؤال، عندما يقوم الإنسان بالإدخال.
ويتضح أنه-- وهذا ليس واضحًا.
وهذه ليست أفضل تجربة للمستخدم على لوحة المفاتيح للشخص.
لكن يمكننا في الواقع اكتشاف الآراء التالية--
إذا قام المستخدم بإدخال أرقام، ثم دعونا نمضي قدما ونقسمها.
ولكن السؤال هنا هو، كيف يمكنك التعبير عن هذا الرمز الزائف؟
حسنًا، يمكنك في بعض البرامج ربما كتابة q للخروج.
ولكن هل ينجح هذا عند استخدام getInt؟
هل يمكننا اكتشاف q؟
لمَ لا؟
الجمهور: لأن getInt يطلب منك على الفور عدد صحيح آخر.
ديفيد ج. مالان: بالضبط.
لأن GetInt يطلب منك على الفور عدد صحيح آخر.
وبسبب الطريقة التي صممنا بها مكتبة CS50، لا يمكنك اكتشاف q،
أو لا يمكن أن يكون لديك النوع البشري خروج، إلا إذا كنت لا تستخدم getInt.
ماذا تستخدم بدلاً من ذلك؟
الجمهور: getString.
دايفيد ج. مالان: يمكننا استخدام getString.
ومن ثم في كل مرة يكتب فيها الشخص رقمًا، يمكننا استخدام،
على سبيل المثال، A2i لتحويله إلى عدد صحيح.
ولكن إذا كان الشخص يكتب q أو خ-ر-و-ج--
سلسلة أيضًا-- يمكن أن يكون لدينا شرط إذا باستخدام سلسلة المقارنة والخروج.
لكن بصراحة، أنت بعد ذلك تعيد تنفيذ getInt--
لذا هي مقايضة.
على أي حال، ستكون الطريقة المشتركة للعمل بشأن هذا،
هي معرفتك أن برامج خروج Control-C، ربما،
يتم إلغاؤها من برنامجك.
هناك ضغطات مفاتيح أخرى شائعة، Control-D،
التي ترسل ما يسمى بنهاية الملف.
إنها تحاكي نهاية الملف.
وتحاكي نهاية مدخل الشخص.
لذا هي مثل النقطة في نهاية الجملة الإنجليزية.
لذا إذا كنت تريد إرسال إشارة إلى جهاز كمبيوتر ينتظر مدخلاً منك
ولا تريد أن يخرج من البرنامج -- والذي سيكون Control-C--
ولكنك تريد فقط أن تنهي إدخال مدخل إلى الكمبيوتر،
فتضغط على Control-D، المعروف أيضًا باسم EOF.
وهو طريقة للتعبير عن هذا-- وستعرف ذلك فقط من
الوثائق-- والتي ستكون كقول شيء من هذا القبيل،
إذا كان الرقم الذي كتبه الشخص يعادل نهاية ملف--
لكن لا يوجد شيء كهذا في هذا السياق--
تقوم بذلك بالفعل لأن مكتبة CS50 تعمل.
وتبين أنه إذا كانت القيم الوحيدة التي يمكن أن تعيدها دالة
هي أعداد صحيحة، وهذا يعني أنه يمكنك إرجاع 0، 1، -1، 2 مليار،
-2 مليار معطاة أو مأخوذة.
ما فعل البشر لسنوات باستخدام لغات البرمجة القديمة
هو أنهم سرقوا رقمًا واحدًا أو بضعة أرقام فقط.
على سبيل المثال، قد تسرق الرقم 2 مليار وتطلق عليه intmax--
الحد الأقصى للعدد الصحيح.
وستقول فقط، أنه لا يمكنك أبدًا كتابة 2 مليار بالفعل،
لأننا نستخدم ذلك كقيمة خاصة للدلالة
على أن البشر ضغطوا على Control-D. أو يمكنك كتابة -2 مليار،
أو يمكنك كتابة 0، أو 50.
ولكن في مرحلة ما، يتعين عليك سرقة رقم واحد من أرقام 4 مليارات المتاحة
لاستخدامها كقيمة مُطلقة، قيمة خاصة
يمكنك بعد ذلك فحصها كقيمة ثابتة.
إذن على أي حال، هذا يعني فقط، عندما ينتهي المستخدم من كتابة مدخل،
امضي قدمًا وقم بتقسيم هذا أثناء التكرار الحلقي.
وكملاحظة جانبية، دعوني أقوم بإصلاح شيء واحد.
تبين أن الأمور يمكن أن تسوء باستخدام realloc.
وإذا فشل realloc في تخصيص ذاكرة، فيمكنه
إرجاع فارغ، قيمة خاصة تعني فقط، آه، حدث خطأ ما.
إنه مؤشر غير صالح.
إنه العنوان 0.
وهكذا تبين أن هناك خطأ خفي هنا حيث، من الناحية الفنية، ينبغي
أن أقوم بهذا في الواقع--
تخزين قيمة معادة من realloc في متغير مؤقت.
لأنه إذا كان temp = فارغ، فقد حدث خطأ ما.
ويجب أن أمضي قدمًا وأخرج من هذا البرنامج بالفعل.
ولكن دعونا لا نتطرق لهذا الآن لأن لديه أكثر من حالة حرجة.
ولكنكم سترون في إصدار هذا البرنامج عبر الإنترنت أننا تحققنا من خطأ إضافي
الذي يفحص فقط، في الحالة النادرة التي يفشل فيها realloc،
وتنظيفه والإرجاع بشكل صحيح.
ولكن سأنتقل إلى التعليمات البرمجية عبر الإنترنت من أجل ذلك.
حسنًا.
أي أسئلة على هذا المثال قبل أن نمضي قدمًا؟
أجل؟
الجمهور: إذن في realloc، عندما تقوم بإنشاء المؤشر الجديد لـ [INAUDIBLE]،،
هل تقوم بمحو الذاكرة من المؤشر الأصلي؟
هل يتم محوها تلقائيًا؟
ديفيد ج. مالان: سؤال جيد.
عندما تستدعي realloc وينتهي الأمر بتخصيص مساحة أكبر،
هل تمحو الذاكرة الأصلية؟
لا.
وهذا هو المكان الذي تأتي منه القيم الضئيلة، على سبيل المثال.
لأنهم فقط قم تم تركهم في الذاكرة من الاستخدام السابق.
أي أسئلة أخرى؟
أجل؟
الجمهور: ماذا يكتب المستخدم في الواقع للتقسيم؟
ديفيد ج. مالان: أوه، Control-D. Control-D. وهو ليس تقسيمًا.
وإنما لإرسال نهاية ملف، نهاية مدخل.
يوقف Control-C أو يُعطّل البرنامج نفسه.
الجمهور: وهل هذا يبدو مثل intmax؟
ديفيد ج. مالان: تمامًا مثل intmax؟
نعم.
الحضور: لأنك لا تقوم بإضافة، على سبيل المثال، قيمة عملاقة.
ديفيد ج. مالان: صحيح.
في مكتبة CS50، intmax، نعم، هو الرمز.
حسنًا.
أجل؟
الجمهور: هل يمكنك أيضًا فقط الطلب من المستخدم قول،
هل تريد أن تدخل رقمًا آخر نعم أم لا؟
ديفيد ج. مالان: بالتأكيد.
يمكننا جعل الأمر منطقيًا أكثر.
ويمكنك استخدام getString.
ويمكننا أن نطلب منه أو منها، مرحبًا، هل تريد إدخال رقم آخر.
الجانب السلبي الوحيد من هذا سيكون، الآن، يجب
أن أكتب ليس فقط رقمي، ولكن نعم أو لا باستمرار.
إذن إنها مجرد واجهة مستخدم مقايضة.
حسنًا.
إذن دعوني أمضي قُدمًا.
ودعوني أمضي قدمُا وأعيد 0 هنا تمامًا مثل حلي البسيط
لهذه المشكلة المتعلقة بحدوث شيء خاطىء.
لقد قمت للتو بتحويل هذا البرنامج برمجيًا.
دعوني أمضي قُدمًا وأشغّله.
سأكتب رقمًا واحدًا، رقمين، ثلاثة أرقام.
والآن أشعر بالملل.
لا أريد الاستمرار في فعل هذا.
كيف أخبر الكمبيوتر أنني انتهيت؟
الجمهور: Control-D.
ديفيد ج. مالان: Control-D. عذرًا.
أوه، حسنًا.
هذا هو التصرف الصحيح لأنني نسيت خطوة رئيسية.
ما هي؟
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: أجل.
أنا لا أقوم في الواقع بفعل أي شيء باستخدام القيم.
ربما يجب أن أقوم بذلك لـ int I تساوي 0، وI أقل من حجم،
التعليمات البرمجية I + + التي كانت لدينا من قبل.
ومن المحتمل أنه يجب أن أطبع لقد قمت بإدخال %I، هذا.
حفظ ذلك.
Make list1.
إذن كل ما فعلته هو إعادة إضافة تعليمات الطباعة البرمجية.
الآن إذا أعدت تشغيل هذا-- واحد، اثنين ثلاثة، Control-D--
اللعنة.
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: أوه.
حسنًا.
الآن عطلّت تعليماتي البرمجية هنا.
دعوني أقوم بهذا.
سنتخلص من فحص الخطأ هذا
لأنني لن أقوم بتغيير الحجم في الواقع.
تحصل الأرقام على realloc.
أوه، وربما يتناغم شخص ما باستخدام هذا--
أرقام قوس حجم يساوي مدخل المستخدم.
الحجم + +-- كان هذا هو التفصيل الرئيسي الذي أراد شخص أن أقوم به؟
حسنًا.
لذا لم أنهي في الواقع البرنامج في وقت سابق.
لاحظوا أننا حذفنا ما يلي--
مرحبًا، جهاز الكمبيوتر، أعطني مصفوفة من الحجم 0 في البداية
هذا فارغ-- لا توجد ذاكرة لذلك.
وبالتالي، حجم هذه المصفوفة هو 0.
قم بما يلي للأبد.
احصل على رقم من الشخص.
إذا كان الرقم يساوي هذه القيمة الخاصة، وتعطّل intmax فقط
بسبب إنهاء البرنامج.
وفي الواقع، معذرة.
وهذا هو سبب قيامي بكتابة هذا مسبقًا أيضًا.
حسنًا.
امضي قدمًا واطلب رقمًا من المستخدم.
إذا قام بإدخال Control-D، فقط قم بتقسيم هذا التكرار الحلقي.
ومع ذلك، إذا كان حجم المصفوفة يساوي سعتها الحالية،
امضي قدمًا وأعد تخصيص مساحة لهذا الشيء والذي يكون رقمًا واحدًا أكبر مما
كان عليه في السابق.
الآن، لنفترض أن الأمر نجح ولدينا ذاكرة،
امضي قدمًا، وفقط مثل list0 لدينا، قم بالتخزين في مصفوفة الأرقام
في الموقع الحالي، الذي هو 0، مهما كان الرقم الذي يكتبه الشخص.
ثم قم بزيادة الحجم بمقدار واحد لتذكر ما قمنا به.
أنا أيضًا وعلى الرغم من ذلك سأحتاج إلى إنشاء سعة + + هنا
لتذكر ما قمنا بزيادة سعة المصفوفة.
إذن مجددًا، مقياسان جديدان.
السعة هي مقدار المساحة هناك إجماليًا.
الحجم هو مقدار ما نستخدمه.
يصادف أنهما متطابقان في الوقت الراهن
لأننا ننمي هذا الشيء خطوة بخطوة بخطوة.
حسنًا.
دعوني أمضي قدمًا وأضغط على حفظ.
دعوني أمضي قدمًا وأحول هذا برمجيًا لآخر مرة.
./list1 والمدخل 1 و2 و3.
Control-D. حسنًا.
الآن أصبح مجرد خطأ جمالي.
لقد نسيت /n الخاص بي.
إذن فقط لإثبات أنه يمكنني في الواقع تشغيل برنامج، ./list1؛ 1، 2، 3؛ Control-D.
بكل سرور.
حسنًا.
إذن قمت بإدخال 1.
والسبب وراء أنه لم ينتقل إلى سطر آخر
هو لأن Control-D يساوي الإرسال على الفور دون الضغط على Enter.
حسنًا.
بكل سرور.
تستخدم جميعها المصفوفات.
الآن دعونا نقوم بأمر قمنا به بالفعل وننتهي منه.
المثال الثالث والأخير هنا هو list2.
وفي الواقع، قبل أن نصل إلى هناك، دعوني أوضح شيئًا واحدًا.
نعم، لنقم بشيء آخر هنا.
دعوني أمضي قدمًا وأشغل، أولاً، صديقنا الجديد valgrind في list1.
Enter.
إنه في انتظاري ليقوم بكتابة 1، 2، 3.
دعوني أمضي قدمًا وأضغط على Control-D. مثير للاهتمام.
يبدو أن لديّ برنامج خاطىء على الرغم من أنني زعمت منذ لحظات أنني
أعرف ما كنت أفعله.
بالتأكيد تم فقدان 12 وحدة بايت في كتلة واحدة في السجل المفقود 1 من 1.
مجددًا، لا أفهم معظم هذه الكلمات.
لكن من المؤكد أنه تم فقدان 12 وحدة بايت--
ربما هذا خطأي. لمَ هي 12؟
وما هي 12 وحدة بايت تلك؟
أجل؟
الجمهور: أعتقد أنك قدمت ثلاثة أعداد صحيحة.
ديفيد ج. مالان: نعم، 1، و2، و3.
الجمهور: وكل واحد عبارة عن 4 وحدات بايت.
وأنت لم تقم بتحرريهم مطلقًا بعد استخدام malloc.
ديفيد ج. مالان: بالضبط.
لقد كتبت ثلاثة أرقام--
1، و2، و3.
كل واحد يمثل 4 وحدات بايت على جهاز الكمبيوتر هذا.
هذا 12 - 3 ضرب 4.
وهكذا فإن أمر تحريري لهم لم يكن مصدر المشكلة مطلقًا.
لذا في النهاية، دعونا فقط نثبت أن valgrind
يمكنه الكشف عن الصحة أيضًا.
تحرير أرقامي، فاصلة منقوطة.
دعوني أمضي قدمًا وأعيد تشغيل make list1.
والآن دعوني أقوم بزيادة حجم هذا وأقوم بإنشاء valgrind مجددًا
على list1، كتابة نفس القيم--
1، و2، و3.
Control-D. تم تحرير جميع الكتل.
لا توجد تسريبات ممكنة.
إذن مجددًا، valgrind هو صديقك.
يجد المشاكل التي لم تلاحظها بالضرورة.
ولم يجب عليك قراءتها خلال سطور تعليماتك البرمجية مرارًا
وتكرارًا لتحديد مصدر المشكلة دون داعٍ.
حسنًا.
أي أسئلة بعد ذلك على تلك المصفوفات التي يتم تخصيصها بشكل ديناميكي
والأخطاء التي نجدها فيها باستخدام valgrind؟
حسنًا.
إذن سيكون الإثبات الأخير من التعليمات البرمجية هو هذا.
لقد استعرت، لهذا المثال الأخير، بعض الكتل البنائية
التي كانت لدينا على الشاشة في وقت سابق.
في تعليماتي البرمجية لـ list2.c، أحتاج إلى بنية تسمى عقدة.
وتلك العقدة، كما زعمنا في وقت سابق مع الأشخاص المتطوعين،
ستحتوي على رقم يسمى رقم،
كما سنطلق عليه هذه المرة، بدلاً من n.
وسيحتوي على ptr يطلق عليه next للعقدة الأخرى تلك.
لذا تم نسخ ذلك ولصقه في وقت سابق، وإن كان باستخدام العدد الصحيح الذي تمت إعادة تسميته
برقم من أجل الوضوح.
الآن، لاحظوا ما سأفعله في main أولاً.
امضي قدمًا وخصّص مصفوفة دون أي مساحة في البداية.
إذن بدا هذا مثلما كانت كومي تمسك الأول وتمثل
بداية بنية البيانات لدينا.
هذا هو التناظر باستخدام مصفوفة، أن قطعة الورق التي
سيتم عدّها هنا ستكون الأرقام.
وهي فقط لا تشير إلى شيء، فارغ-- مثل اليد اليسرى
التي تشير إلى الأرض.
لأنه لم يتم تخصيص ذاكرة بعد.
ولكن بعد ذلك، في while true، امضي قدمًا واحصل على عدد صحيح
من المستخدم باستخدام تلك التعليمات البرمجية هنا.
تحقق إذا كان المستخدم ضغط على Control-D، كما هو الحال مع هذه التقنية الغامضة.
ثم تصبح تعليماتنا البرمجية مشابهة جدًا في جوهرها، لكن
يتعين علينا تجميع تلك الأشياء معًا.
خصّص مساحة للرقم.
لذا عندما قمت بتخصيص متطوع إضافي من الجمهور
وقام أو قامت بالمجيء، المعادل في التعليمات البرمجية هو هذا--
مرحبًا، جهاز الكمبيوتر، قم بتخصيص مساحة كافية لتناسب حجم العقدة باستخدام malloc،
ثم قم بتخزين النتائج في ptr التي يطلق عليه n.
إذن node *n تشير فقط إلى، أعطني مؤشر إلى عقدة، وأطلق عليه n،
وقم بتخزين العنوان الذي قمنا للتو بتخصيصه من الجمهور كما فعلنا من قبل.
لماذا لدي هذه السطور من التعليمات البرمجية هنا التي ميزتها باللون الأزرق؟
ما الذي يعبر عنه هذا؟
إذا إشارة التنبيه n، إذا ليس n كيف سيكون نطقها--
ما الذي يحدث هناك؟
أجل؟
الجمهور: إذا لم يوجد المزيد من الذاكرة التي يمكنك الإشارة إليها، فهذا يعني أنه فشل.
ديفيد ج. مالان: بالضبط.
هذا لن يحدث كله في كثير من الأحيان.
لكن إذا نفدت الذاكرة من جهاز الكمبيوتر، وبالتالي سيفشل malloc،
أنت لا تريد أن يتعطل البرنامج أو يتوقف عن العمل فقط.
على سبيل المثال، كلنا نكره الأمر عندما يحدث ذلك على Mac OS أو Windows.
لذا تحقق من ذلك.
إذا ليس n، أو معادلاً لها، إذا n = = فارغ، فأعد 1 فقط.
واخرج بشكل لائق، وحتى لو كان بشكل مزعج.
لكن لا تقم بتعطيله أو لا تقم بشيء غير متوقع فقط.
لذا يمكنك تبسيط هذا الفحص إلى فقط إذا ليس n-- إذا كان n ليس ptr صالحًا،
أعد 1.
الآن، ها هي التعليمات البرمجة التي كنا ننفذ الإثبات باستخدامها
مع هؤلاء المتطوعين.
وهذه هي النظرة الأكثر رعبًا أو الأكثر غموضًا على الأقل
للتعليمات البرمجية التي سنراها في C.
اليوم هو يومنا الأخير في C. لقد قمنا بتشغيل
تلة شديدة الانحدار في الآونة الأخيرة، وتعلمنا عن الذاكرة،
والآن بنيات البيانات والصيغة.
هذه هي آخر صيغة في C.
إذن ما هي الرموز التي يجب أن تكون على دراية بها؟
سطر التعليمات البرمجية هذا هنا هو كيف أسلم أحد المتطوعين قطعة من الورق.
على الجانب الأيمن، الرقم الذي تمت كتابته--
55، أو 5، أو 20، أو أيًا كانت القيمة.
على الجانب الأيسر هو حيث تريد وضعه.
n ومن ثم حرفيًا رقم السهم الذي يقوم بهذا.
لديه، باستخدام malloc، سطر أو ما كان قبل ذلك، المعطى في الذاكرة
أحد هذه المستطيلات الكبيرة فقط.
ومرة أخرى، يُطلق على الجزء العلوي من هذا في هذا المثال الرقم
ويطلق على الجزء السفلي next.
إذن هذا هو الشخص الذي وقف بنهاية الغرفة.
عندما أسلم هذا الشخص رقم، مثل 55، فإنه ينتقل إلى هناك بشكل مرئي.
سطر التعليمات البرمجية الذي تحقق ذلك باستخدامه هو هذا هنا.
لأنه لاحظوا في السطر 31 هنا، عندما أقوم بتخصيص تلك العقدة،
قمت بتخزين عنوانها في متغير يسمى n.
وهذا مؤشر، كما هو مرسوم باستخدام سهم، إلى هذه العقدة الكبيرة.
أو إذا كنا نريد حقًا أن نصعب الأمر، إذا كان هذا في عنوان 100،
نعم، لذا لدى المؤشر في الواقع القيمة 100 بداخله.
ولكن مجددًا، وهذه معلومة مفيدة نادرة.
لذا يمكننا فقط التجريد باستخدام سهم فقط.
لذا، فإن السطر 31 هو الذي ينشئ تلك المربعات على الشاشة.
السطر 38 هو الذي يضع الرقم--
على سبيل المثال، 55-- في المربع بالضبط، مثلما سلمت قطعة
من الورق.
إذن ما هذا؟
هذا هو الترميز الجديد الحقيقي الوحيد اليوم،
على الرغم من أننا نستخدم الكثير من النجوم في مكان آخر--
سهم هذه هي المرة الأولى بشكل رائع في C التي نقوم بتعيينه فعليًا على صورنا.
إذا كان n هو المتغير، فأنت تقوم بكتابة n سهم إلى شيء ما،
وهذا يعني متابعة السهم--
شيء مثل السلم والثعبان إذا ترعرعت وأنت تلعب تلك اللعبة--
ومن ثم ضع الرقم الذي قادك إليه السهم في الحقل الذي يطلق عليه رقم.
لذا كملاحظة جانبية، يمكننا التفكير في هذا بطريقة مختلفة. n هو أي نوع من أنواع البيانات؟
ما هو هذا الشيء الموجود باللون الأزرق--
n؟
الجمهور: مؤشر.
ديفيد ج. مالان: إنه مؤشر.
وهو مؤشر إلى أحد هذه الأشياء التي قمنا بإنشاءها في وقت سابق.
لذلك نحن لا نقوم بإنشاء students بعد الآن باستخدام بنياتنا.
نحن ننفّذ العقد، والتي تحتوي على أرقام ومؤشرات next.
لذا يتضح أنه إذا كانت n مؤشر إلى عقدة--
تذكرون تدوين النقطة من قبل--
هذه ليست طريقة الوصول إلى رقم في هذه الحالة.
لأن n ليس عقدة بنفسها.
إنه مؤشر.
ولكن إذا كان n مؤشر، كيف يمكنك الانتقال إلى مؤشر؟
كيف يمكنك الانتقال إلى عنوان؟
باستخدام أي تدوين؟
الجمهور: نجمة.
ديفيد ج. مالان: نجمة.
لذا تذكرون أنه من الأسبوع الماضي، إذا كنا نريد الانتقال إلى عنوان،
يمكنكم القيام بصيغة من هذا القبيل.
تجاهلوا الأقواس للحظة.
فقط *n تعني إذا كان n هو عنوان جزء من الذاكرة، *n تعني الانتقال إلى هناك.
وبمجرد أن تكون هناك، ستكون على صواب من الناحية النظرية هنا-- أعلى الزاوية اليسرى.
كيف يمكنك الوصول إلى حقول فردية مثل رقم أو next؟
تستخدم تدوين النقطة.
إذا كنت تقوم حرفيًا بإنشاء *n.number، هذا يعني الانتقال إلى العنوان والوصول
إلى حقل الرقم.
توجد صيغة لطيفة في C، وهي
مجرد طريقة خيالية لقول تدوين شديد الاقتضاب، حيث إنه مجرد السهم.
لكن هذا هو كل ما فيه.
تدوين السهم هذا لا يقوم بأي شيء جديد.
إنه يقوم فقط بالتجميع، الانتقال إلى هناك، باستخدام، الوصول إلى حقل في struct، جميعًا في نفس واحد
إذا صح التعبير.
وهذا فقط يبدو أجمل قليلاً.
عندما قلت للمتطوعين في وقت سابق، أشيروا بأيديكم
إلى الأرضية، هذا كل ما يفعله هذا السطر من التعليمات البرمجية.
إنه يقول، انتقل إلى عنوان n، الموجود هنا، وقم بالوصول إلى حقل next،
واكتب في هذا الحقل فارغ، وهو فقط
العنوان 0-- العنوان الافتراضي الخاص، مثل الإشارة إلى الأرضية.
هذا السطر من التعليمات البرمجية، 40، هو فقط فحص خطأ سريع.
if (numbers)---- ما الذي يعادل ذلك؟
هذا يقول في الواقع فقط، if numbers، لا يساوي فارغ.
لذا إذا كانت الأرقام صحيحة، إذا كان malloc يعمل بشكل صحيح، إذن دعونا نمضي قدمًا
ونقم بما يلي.
بكل سرور.
هذا هو النطق.
ما الذي يحدث هنا؟
إذن هذا هو التكرار الحلقي الذي لا يستخدم أرقام.
حسنا ، أم هل يستخدم؟
تقريبًا كل تكرار حلقي قمنا بكتابته وربما كنت قد كتبته للتو
باستخدام I، J، ربما K، لكن من المحتمل أعداد صحيحة فقط.
لكن لا يجب أن يكون هذا هو الحال.
ما هو المؤشر؟
إنه عنوان.
ما هو العنوان؟
الجمهور: مكان في الذاكرة.
ديفيد ج. مالان: مكان في الذاكرة، أو رقم بالفعل.
لذا يمكنك بالتأكيد استخدام التكرارات الحلقية فقط لتضمين عناوين.
ولكن ما هي الكيفية؟
لذلك سنأخذ هذا السطر من التعليمات البرمجية بعين الاعتبار.
هذا يبدو مختلفًا هنا اليوم، لكنه كل شيء
قبل تلك الفاصلة المنقوطة الأولى.
هذا فقط حيث يمكنك تهيئة قيمة.
إذن هذا مثل قول، مرحبًا، مرحبًا جهاز الكمبيوتر، امضي قدمًا وأعطني
متغير يُسمى ptr وقم بتهيئته ليكون بداية القائمة الخاصة بي.
ثم أقول، مرحبًا، جهاز الكمبيوتر، قم بهذا طالما ptr لا يساوي فارغ.
وبعد ذلك ماذا سأفعل؟
إذا-- ودعونا نتجاهل هذا في الوقت الحالي، إنه فحص خطأ--
امضي قدمًا و-- معذرة، دعوني أفكر لثانية واحدة.
حسنًا.
لنقم بذلك.
ماذا تفعل هذه السطور من التعليمات البرمجية؟
هذه هي التعليمات البرمجية التي تم اقتراحها بالفعل
في نهاية مثالنا البشري.
على سبيل المثال، ماذا لو أردنا إدراج جميع العناصر
في نهاية قائمة الارتباط؟
كيف يمكن التعبير عن ذلك؟
إذن في هذه الخطوط المظللة من التعليمات البرمجية، نحن نطرح السؤال،
إذا كان حقل next للمؤشر الحالي هو فارغ، فقد وجدنا النهاية.
امضي قدمًا وقم بتحديث حقل next هذا ليصبح مساويًا لـ n ومن ثم break.
إذن دعوني أترجم هذا إلى صورة حقيقية،
ولكن باستخدام مربعات أصغر توضح الأمر عندما يحدث شيء.
لذا لنفترض أن هذا البرنامج تم تشغيله لبعض الوقت
ولدينا قائمة مستفيضة تبدو بهذا الشكل،
حيث يشير هذا إلى هنا وربما يشير هذا إلى هنا.
وهذا يقول فارغ هنا.
وهذا يشير إلى هنا.
والأرقام هي، كما كنا نستخدمها اليوم، 42، 50، 13.
لذا يطلق على بداية هذه القائمة أرقام.
هذا يشير إلى بداية القائمة.
ماذا أفعل في هذا التكرار الحلقي؟
أنا أقوم فقط بتنفيذ المنطق التالي باستخدام التكرار الحلقي--
أعطني متغير يُسمى Ptr، كما هو مُمثل
في القصة في اتجاه إصبعي الأيسر، هنا، وقم بتهيئة
هذا ليكون بداية القائمة.
إذا كان مؤشر next للعقدة يساوي فارغ، أضف عقدة جديدة هنا.
لكنه لا يساوي فارغ.
أريد تتبع الآثار إلى هنا.
ومن ثم، أوه، نحن في نهاية القائمة.
أريد إدراج هذا الشيء الجديد هنا.
إذن كيف يمكننا التعبير عن هذه التعليمات البرمجية في C في الواقع؟
لذا إذا نظرت إلى الوراء هنا، هذا هو سطر التعليمات البرمجية
الذي يخصص إصبعي الأيسر هنا المُسمى ptr ويقوم بتهيئته
ليصبح مساويًا للأرقام، وهو الأمر نفسه عند الإشارة إلى العنصر الأول.
يبدو مثل ما كانت تمثله كومي في وقت سابق.
ولكن الآن يطلق على مصفوفتنا أرقام.
وبعد ذلك ماذا سأفعل؟
هل ptr يساوي فارغ؟
حسنًا، لا.
إذا كانت يدي اليسرى تشير إلى هنا، فإنه من الواضح لا يساوي فارغ.
لذلك لا داعي للقلق بعد الآن.
ثم ماذا أريد أن أفعل؟
إذا كان ptr next يساوي فارغ، حسنًا، فما الذي يعنيه ذلك؟
حسنًا، Ptr هنا.
يشير ptr سهم next إلى هنا.
هل هذا يساوي فارغ في هذه القصة؟
ما أعنيه، أنه لا يساويه حرفيًا.
لأن فارغ لا يُكتب هناك. فارغ موجود هناك في الأسفل.
إذن لم يتحقق الشرط.
إذن ماذا أفعل بعد ذلك؟
إذا كان ptr يساوي null لا ينطبق، فها هو تحديث غريب.
ptr يساوي ptr next.
إذن هذه صيغة تبدو مشفرة.
ولكن إذا كان ptr يشير إلى هنا، فما هو ptr next؟
إنه هو هذا فقط، أليس كذلك؟
هذا هو n.
هذا هو next.
أو هذا هو رقم.
هذا هو next.
إذن هذا هو ptr next.
إذن ما هي تلك القيمة؟
حسنًا، هذا مؤشر يشير إلى هنا.
إذن كتلة التعليمات البرمجية المُظللة هذه، ptr يساوي ptr next،
لديه تأثير بشكل مرئي للقيام بذلك.
لماذا؟
إذا كانت الأسهم هي سحرية تمامًا قليلاً، فكّروا فقط في هذه باعتبارها عناوين.
إذا كان هذا يقول،عنوان next هو الموقع 100،
ptr يساوي ptr next مثل قول، حسنًا، هذا أيضًا يساوي 100.
أيًا كانت 100، على سبيل المثال، هنا
ما الذي يجب أن يشير إليه السهمان الآن.
وإذا كررت الآن هذه العملية وكررتها مجددًا،
ففي النهاية يكون السؤال الذي سألته في وقت سابق سيطبّق--
إذا Ptr next يساوي فارغ، ما الذي أريد فعله؟
حسنًا، إذا كان Ptr next يساوي فارغ، ويوجد سطران ينتقلان. ptr next يساوي n.
إذن لم يعد ptr next هو فارغ.
يجب أن يشير بدلاً من ذلك إلى n، وهو العقدة الجديدة.
ومن ثم هذا كل شيء.
لأنه تمت تهيئة هذا بالفعل لفارغ.
ودعونا نفترض أن هذا كان 55.
ها قد انتهينا.
من السهل جدًا القيام به، هذا واضح، بشكل شخصي مع أفراد فقط،
والتحرك، والإشارة باستخدام أيديهم اليسرى.
ولكن في التعليمات البرمجية، يتعين عليك فقط التفكير في كتل البناء الأساسية.
ما هي كل هذه القيم؟
إلى أين تشير كل واحدة منها؟
وأي من هذه الحقول تحتاج إلى تحديثها؟
والتعليمات البرمجية الوحيدة هنا-- حتى لو قمنا بتجميعها كلها في
مثال هائل--
هي هذه.
نحن في الواقع نستخدم تدوين السهم لنقول، انتقل إلى هذا العنوان
وقم بالوصول إلى قيمة فيه.
وهذا الشرط هنا، الذي، لن أتطرق إليه الآن،
يتعامل فقط مع هذا الموقف حيث تكون القائمة فارغة في البداية.
أي أسئلة أخرى حول هذا حتى الآن؟
حسنًا.
إذن دعونا نلقي نظرة بصورة بيانية أكثر على بعض المشاكل النهائية التي يمكننا حلها.
والذي سترونه في الأيام المقبلة هو كما يلي
عندما يتعلق الأمر بهذه القوائم المرتبطة وأكثر من ذلك.
لدينا الآن القدرة بالفعل على تخصيص الأشياء في الذاكرة بشكل ديناميكي.
نحن لا نعرف بالضرورة مسبقًا كم عدد الأرقام الموجودة لدينا
أو، في حالة مجموعة المشاكل التالية، كم عدد الكلمات الموجودة لدينا.
لدينا القدرة رغم ذلك على استخدام malloc، وربما حتى realloc،
لنزيد وننمّي بنية البيانات في الذاكرة.
ولدينا القدرة في التعليمات البرمجية لاجتياز
تلك القيم بالفعل بطريقة يمكننا
من خلالها الوصول إلى الذاكرة الموجودة كلها على اللوحة الآن
وليس بالضرورة العودة على التوالي.
ولكن ما الذي سيحدث إذا كنا مازلنا نريد جمع هذه الأفكار
بحلول أفضل؟
حسنًا، دعونا نلقي نظرة على ذلك.
وتحديدًا، إذا انتقلت دوعنا نقل إلى هنا إلى ما يلي،
دعونا نفكر في مشكلة يمكننا حلها الآن.
إذا أردت تخزين اسم كل شخص في هذه الغرفة في بنية البيانات،
يمكنني القيام بماذا؟
حسنًا، يمكننا استخدام مصفوفة.
إذن يمكنني في الواقع أن أقرر كم عدد الأشخاص في الغرفة--
دعونا نطلق عليه n--
ونرسم مربعات n بالفعل على اللوحة، ومن ثم نطلب بشكل متكرر
اسم كل شخص، وندوّنه هنا بالفعل.
إذا أردت أخذ الحضور بعد ذلك وأقول، أوه، هل أليس موجودة هنا،
أو بوب هنا، أو كريم هنا، أو براين،
يمكنني فقط النظر في تلك المصفوفة وقول نعم أو لا، هذا الشخص موجود هنا.
ولكن ما هي مدة تشغيل هذه الخوارزمية؟
كم من الوقت سيستغرق البحث عن الاسم في بنية البيانات
حيث قمت برسمها كمصفوفة فقط، قائمة كبيرة على اللوحة؟
الجمهور: حرف O كبير من n.
ديفيد ج. مالان: ما هذا؟
الجمهور: حرف O كبير من n.
ديفيد ج. مالان: حرف O كبير من n، أليس كذلك؟
لأنه إذا كانت مجرد قائمة أسماء، فستأخذ حرف O كبير من n.
وبصراحة، يبدو ذلك أمرًا بطيئًا بعض الشيء.
كيف يمكنني القيام بتحسين؟
حسنًا، ماذا لو قمنا بجمع بعض هذه الأفكار؟
المصفوفات جيدة لأنها تمنحني ترتيبًا عشوائيًا
من الوصول الفوري إلى مواقع الذاكرة.
لكن القوائم المرتبطة جيدة لأنها تتيح لي إضافة عناصر أو طرحها
بشكل ديناميكي حتى لو أردت ذلك من القائمة.
إذن أتعلمون ماذا؟
بدلاً من كتابة اسم كل شخص، مثل أليس وبوب،
وتشارلي، وما شابه ذلك في مصفوفة واحدة كبيرة فقط من حجم ثابت قد
يبقيني في زاوية - الآن لديّ فقط مساحة لاسم واحد آخر--
ماذا لو قمت بدلاً من ذلك بأشياء بصورة أكثر ذكاء؟
إذن في الواقع عندما أدون اسم كل شخص في الغرفة، ماذا
لو كتبت بدلاً من ذلك، حسنًا، هل أليس موجودة هنا.
حسنًا.
أليس موجودة هنا.
ثم براين هنا.
سأضع اسم براين هنا.
ومن ثم ربما تشارلي هنا.
حسنًا.
إذن تشارلي.
ومن ثم ربما أرنولد هنا.
أين يجب أن أضع أرنولد؟
إذن يبدأ أيضًا بالحرف أ. أتعلمون ماذا؟
دعونا فقط نضع أرنولد هنا.
أرنولد.
وآبي موجودة هنا.
إذن أتعلمون ماذا؟
دعونا فقط نضع آبي هنا أيضًا.
لقد حضر بوب كذلك.
إذن بوب-- إذن ما هو النمط الذي أتبعه بوضوح
حيث أسمع الأسماء التي ينادى عليها؟
الجمهور: مرتبة أبجديًا.
ديفيد ج. مالان: مرتبة أبجديًا--
نوعًا ما.
مثلما، انتهى المطاف بآبي في مكان غريب هنا.
لكن لا بأس لأنني لم أسمع اسمها أولاً.
لكنني قمت بترتيب الأشخاص نوعًا ما في صفوف مختلفة من اللوحة.
وبعبارة أخرى، جميع الأسماء التي تبدأ بحرف أ يبدو لي
أنني كتبتهم فقط في الأعلى لتيسير الأمر،
ومن ثم جميع الأسماء التي تبدأ بحرف ب معاً، والأسماء التي تبدأ بحرف س.
وربما إذا تابعت، يمكني القيام بذلك على طول الطريق
إلى الحرف ي في الأبجدية العربية.
لذا ما هو الأمر الجيد في هذا هو أن، نعم، أنني أقوم بإنشاء قوائم أسماء،
ولكن ما هو طول كل هذه القوائم؟
إذا كان هناك n من الأشخاص في الغرفة، فلن تكون كل القوائم الخاصة بي
طول n ، وهو بطيء.
سيكون ماذا؟ n مقسومة على 26، معطاة أو مأخوذة.
إذا افترضنا أن هناك عدد متساوي من الأشخاص
وأسماؤهم تبدأ بحرف ي وأ، سيكون تقريبًا n مقسومًا على 26 إذن
لدي هذه السلاسل من أسماء الأشخاص، ولكنها
أقصر بكثير مما سيكونوا عليه إذا قمت فقط بتجميع الكل معًا.
وتعد هذه تقنية أساسية في البرمجة تسمى علامات تجزئة.
اتضح أنه يوجد أشياء في هذا العالم تسمى بداول علامات تجزئة.
هي مجرد دوال رياضية، أو حرفية، أو مُنفذة من تعليمات برمجية
التي تأخذ كإدخال شيء ما وتنتج كإخراج رقم تمامًا-- رقم
من 0 إلى، لنقل، 25، أو من 1 إلى 26.
ولكن يمكنها أيضًا إخراج سلاسل في سياقات أخرى أيضًا.
إذن دالة علامة التجزئة الخاصة بي هنا في عقلي هي، إذا سلمتني اسمًا،
سألقي نظرة على الحرف الأول من اسمك.
وإذا كان أ، سأقوم بوضعك في الموقع 0.
إذا كان ب، سأضعك في الموقع 1.
إذا كان هو ي، سأضعك في الموقع 25 في النهاية.
إذن هذه كل الحزمات الموجودة لديّ، إذا جاز التعبير،
في علوم الكمبيوتر-- على سبيل المثال 26 حزمة أو مساحة
على اللوحة التي تمثل بدايات أسماء الأشخاص.
إذن ما هذا؟
حسنًا، يبدو أنه إذا لم أكن اعرف مسبقًا كم عدد الأسماء التي تبدأ بالحرف أ لدي،
هذا يبدو كرسم لهذه كقائمة مرتبطة، إذا صح التعبير،
وقد يستغرق ذلك وقتًا أطول وأطول.
لكنني أعرف أن لديّ فقط عدد محدود من الأحرف الأولى.
لذلك-- في مجازفة للرسم بصورة عشوائية إلى حد ما--
هو نوع من أنواع الرسم لأي بنية من بنيات البيانات؟
الجمهور: مصفوفة.
ديفيد ج. مالان: أجل.
إنه نوع من أنواع رسم المصفوفة التي بها فقط 26 مكان.
والأمر الجيد بشأن المصفوفة هو أن لديّ وصول عشوائي.
يمكنني الانتقال مباشرة إلى أي حرف من حروف الأبجدية في وقت ثابت، خطوة واحدة.
وبمجرد أن أصل إلى هناك، سأستمر في رؤية قائمة الأسماء.
لحسن الحظ، بفضل القوائم المرتبطة، تلك القائمة التي يمكن أن تكون قصيرة أو طويلة.
ولكن في المتوسط، دعونا نقول أنه سيكون
الطول 126 الأمر الذي سيحدث إذا استخدمت للتو مصفوفة واحدة أو قائمة مرتبطة
واحدة.
إذن تقنية استخدام دالة علامة التجزئة هذه-- والتي قمت، مجددًا،
بتحدديها كما قمتم بإعطائي اسمًا؛ وأخذت ذلك كإدخال؛
وأنظر إلى الحرف الأول؛ وأعيده كإخراج رقم من 0 إلى 25--
تتيح لك دالة علامة التجزئة إنشاء جدول علامة تجزئة.
وهناك طرق مختلفة لتنفيذ جداول علامات التجزئة،
ولكن ربما الطريقة الأكثر شيوعًا هي بالفعل هكذا.
عليك أن تقرر مسبقًا حجم المصفوفة.
لكن هذه المصفوفة لا تحتوي على السلاسل أو أسماء الأشخاص.
تحتوي هذه المصفوفة بالفعل على قوائم مرتبطة.
وهي القوائم المرتبطة التي تحتوي على الأسماء.
إذن نحن نستعير أفكارًا من، على سبيل المثال، الأسبوع الثاني.
نقوم بدمجهم باستخدام فكرة اليوم من الأسبوع الرابع من إضافة المصفوفات
إلى القائمة المرتبطة على التوالي.
ونحصل على الأفضل في كلتا الحالتين.
لأنه يمكني الانتقال فورًا إلى أي حرف من الأحرف الأبجدية بسرعة فائقة.
وعندما أصل إلى هناك، نعم، توجد قائمة،
ولكنها لن تكون قريبة طالما لا أستخدم هذه الخدعة.
إذن ما هو وقت التشغيل لكل هذا؟
حسنًا، تبين أن جدول علامة التجزئة في أسوأ الحالات
قد يظل يستغرق عدد من الخطوات للعثور على اسم شخص بمجرد
أن تتم إضافته إلى القائمة؟
في أسوأ الحالات، كم عدد الخطوات، إذا كان هناك n من الأشخاص في الغرفة؟
الجمهور: n.
ديفيد ج. مالان: ربما n.
لماذا؟
إنه وضع منحرف إلى حد ما.
ولكن هل يمكنك استنباط سيناريو حيث،
على الرغم من أننا نقوم بهذا الأمر الرائع، فما زال
يستغرق n من الخطوات لتأكيد أو إنكار وجود شخص ما هنا؟
أجل؟
الجمهور: يبدأ اسم الجميع بنفس الحرف.
ديفيد ج. مالان: يبدأ اسم الجميع بالحرف نفسه
لسبب ما غريب.
الآن، يصبح الأمر سخيفًا قليلاً في عالم البشر.
ولكن يمكن أن يحدث ذلك إذا كنت تتناول فقط
عن البيانات أو أيًا كان في عالم الكمبيوتر.
يمكن أن يتحول هذا إلى، بالتأكيد، مصفوفة بقائمة مرتبطة واحدة فقط بالفعل.
ولكن في الواقع، ليس من المحتمل أن يحدث هذا، أليس كذلك؟
إذا استغرقنا بالفعل الوقت هنا وطلبنا من الجميع ذكر أسمائهم،
ربما نحصل على توزيع منسق بشكل منطقي للرسائل،
على الأقل كما هو محتمل من الناحية الإحصائية باستخدام أسماء الأشخاص فقط.
إذن هذا عبارة عن نشر للأشياء.
ولذا يوجد هذا التمييز الأساسي بين وقت التشغيل
لعالم حقيقي إلى حد ما، أو وقت ساعة الحائط-- كم عدد الثواني التي تدور بالفعل
في الساعة--
مقابل وقت تشغيل مُقارب.
لقد تحدثنا لبضعة أسابيع حتى الآن حول وقت التشغيل كحرف O الكبير من n.
وقد يظل هذا هو الحال، حيث جدول علامة التجزئة-- نعم، في أسوأ الحالات،
ما يزال حرف O الكبير من بنية بيانات n.
لأنه في أسوأ حالة، سيأخذ خطوات n.
ولكن في العالم الحقيقي، يكون حرف O الكبير من n هو حرف O كبير من n بالفعل مقسومًا على 26،
حتى لو أننا دائمًا ما نتجاهل تلك المصطلحات ذات الترتيب الأدنى.
ولكن عندما تقوم، كشخص، بتشغيل التعليمة البرمجية وتحليل البيانات،
ويعد تشغيلها بمقدار 26 مرة بشكل أسرع في الواقع هو توفير للوقت الحقيقي،
حتى لو قال عالم رياضيات، آه، هذا هو الأساس نفسه.
وبالفعل، إحدى المشكلات التي ستواجهنا في مجموعة المشاكل التالية
هي بالضبط معرفة ما هي الآثار
في تعليمتك البرمجية لوقت تشغيل ساعة الحائط الفعلي.
وإعداد قرارات تصميم أكثر ذكاء، مثل شيء من هذا القبيل،
يمكنه بالفعل تسريع تعليمتك البرمجية لتكون 26 مرة أسرع، حتى لو،
نعم، سيقول باحث في الجانب النظري ذلك، آه،
ولكن هذا ما يزال معادلاً بشكل متقارب أو بشكل حسابي
لشيء خطي فقط.
إذن هذا هو الضبط الدقيق الذي من شأنه أن يحسن تعليمتك البرمجية بشكل أفضل وأفضل.
الآن، بصراحة، من المحتمل أن علامات التجزئة على الأسماء الأولى
ليست الشيء الوحيد الأكثر ذكاء، أليس كذلك؟
على سبيل المثال، هل يوجد أي شخص-- وسيكون هذا صعبًا.
هل يبدأ اسم أي شخص بـ X هنا؟
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: ليست هنا.
لكن شكرًا لك على ذلك المثال المضاد المثالي.
لكنها ليست هنا.
إذن انظروا، لا يوجد أسماء مبدوءة بـ Z.
نحن الآن نصل إلى 25 قيمة ممكنة.
وقد يمكنني ربما اختيار بعض الأحرف الأقل شيوعًا.
المغزى أنه من المحتمل أن يوجد عدد أسماء قليل تبدأ بحرف أ أكثر من التي تبدأ بحرف ي
أو عدد آخر من الأسماء التي تبدأ بحرف ب هناك أكثر من تلك التي تبدأ بحرف ك فقط حسب طبيعة أسماء الأشخاص.
ولذلك ربما يكون استخدام الحرف الأول فقط أمرًا ليس جيدًا بما يكفي.
وبصراحة، باستخدام 26 اسمًا-- لنفترض أننا فعلنا ذلك لجميع الأشخاص في جامعة هارفارد
ولدينا الآلاف من الأسماء.
قد تظل كل سلسلة من سلاسلي تحتوي على المئات أو الآلاف من الأسماء.
لذلك فإن سؤال التصميم الآخر سيكون، حسنًا، كم عدد الحِزم التي يجب
أن تكون لديك، ما هو الحجم الذي يجب أن تكون به المصفوفة.
ربما لا يجب أن تنظروا إلى الحرف الأول.
ماذا لو نظرتم إلى الحرف الأول والثاني معًا-- إذن AA، وAB،
وAC، ومن ثم نقطة نقطة نقطة، BA ،BB ،BC، حتى يمكنكم الحصول
على المزيد والمزيد من الحِزم؟
لكن ماذا أيضًا؟
وإلا كيف يمكننا أن نوزع الأشخاص بشكل منتظم إلى حد ما؟
ما الذي يتوفر لديكم جميعًا يمكن أن نستخدمه كمدخل إلى دالة علامة التجزئة؟
الجمهور: اسم العائلة.
ديفيد ج. مالان: حسنًا.
حسنًا، يمكنك استخدام اسم العائلة، والذي قد يمنحنا
توزيعًا مختلفًا أو مماثلاً.
أجل؟
الجمهور: رقم الهوية.
ديفيد ج. مالان: ماذا؟
الجمهور: رقم الهوية.
ديفيد ج. مالان: أجل.
يمكننا استخدام رقم هويتك وبالفعل نلقي نظرة على الرقم الأول من رقم هويتك.
والاحتمالات هي، من 0 إلى 9.
إذن ربما يمكننا الحصول على 10 حِزم على الأقل بهذه الطريقة.
ومن المحتمل أن هذا تم توزيعه بشكل منتظم.
لست متأكدًا.
يمكننا استخدام تواريخ الميلاد بطريقة ما.
على سبيل المثال، يمكننا وضع كل حديثي الولادة في حِزمة واحد،
والأكبر سنًا في حِزمة آخرى، والآخرين أيضًا،
وهكذا، في الحزمات الخاصة بهم، والتي من شأنها أن تعطينا بعض المدخلات.
إذن مجددًا، دالة علامة التجزئة متروكة لكم تمامًا لتقوموا ببرمجتها وتصميمها.
ومع ذلك الهدف هو تسهيل الأمور.
تريدون أن تحتوي كل قائمة مرتبطة على نفس العدد من الأشياء
فقط حتى يكون لديكم تقريبًا الأداء نفسه
عبر جميع تلك المُدخلات المختلفة.
لذا دعونا نلقي نظرة على زوجين من بنيات البيانات الأخرى،
مجددًا، بهذه الطريقة المجردة.
والآن قد عرفنا أن، على الرغم من أن الأمر لم يكن واضحًا في المحاولة الأولى،
عرفنا كيف نقوم بإنشاء مصفوفات.
نعرف الآن كيفية إنشاء القوائم المرتبطة.
ومن المنطقي أنه يمكننا تنفيذها معًا في التعليمة البرمجية.
ماذا أيضًا يمكننا فعله الآن مع كتل البناء هذه؟
على سبيل المثال، هذه البنية هنا هي بنية شائعة جدًا، تُعرف بالشجرة.
شجرة مثل شجرة العائلة، حيث يوجد هناك أب واحد أو أم واحدة
في الأعلى، ومن ثم أبناؤهم، ومن ثم أحفادهم،
وأولاد أحفادهم، وهكذا.
والأمر الجيد في بنية شجرة هو أنه، إذا كنت تقوم بتخزين البيانات،
يمكنك في الواقع تخزين البيانات بطرق ذكية للطفل الموجود على اليسار،
وللطفل الموجود على اليمين، وهكذا، على النحو التالي.
لاحظوا هنا، يوجد شيء غريب بشأن جميع الأرقام في بنية البيانات
تلك.
ما هو الجدير بالملاحظة بشأنها؟
ما هو الجدير بالذكر؟
أجل؟
الجمهور: مضاعفات الرقم 11.
ديفيد ج. مالان: ما هذا؟
الجمهور: إنها مضاعفات للرقم 11.
ديفيد ج. مالان: هي مضاعفات للرقم 11.
كان ذلك فقط لجعلها تبدو جميلة من قِبل المؤلف هنا.
أجل؟
الجمهور: [INAUDIBLE].
ديفيد ج. مالان: أجل.
هناك دلالة رياضية أيضًا.
مثل، بغض النظر عن العقدة أو الدائرة التي تنظر إليها، فإن القيمة فيها
أكبر من تلك للطفل الموجود على اليسار وأصغر من تلك للطفل الموجود على اليمين.
إذن هي بينهما نوعًا ما.
أي دائرة تنظر إليها، يكون الرقم لناحية اليسار هو أصغر،
والرقم من ناحية اليمين هو أكبر.
وأعتقد أن هذا ينطبق عالميًا في كل مكان.
نعم؟
إذن ماذا يعني هذا؟
سنتذكر من، على سبيل المثال، الأسبوع 0 عندما كانت لدينا مجموعة كاملة من صفحات دليل الهاتف
التي كنا نبحث فيها عن--
1، 2، 3، 4، 5، 6.
دعونا نمنح أنفسنا خانة 7.
تذكرون أنه عندما قمنا بإنشاء خوارزمية فرق تسد، أو البحث الثنائي،
فعلنا ذلك في مصفوفة.
وما كان لطيفًا في البحث الثنائي أننا بدأنا في المنتصف،
وبعد ذلك ربما انتقلنا إلى اليسار، أو ربما انتقلنا إلى اليمين،
وقمنا بإنشاء خوارزمية فرق تسد لحل المشكلة
بشكل أكثر كفاءة في وقت لوغاريتمي
عن ما كان يمكن أن يكون إذا فعلنا ذلك خطيًا.
لكننا نعرف الآن بعد عدة أسابيع أن المصفوفات محدودة نوعًا ما، أليس كذلك؟
إذا تابعت تخزين جميع قيمي في مصفوفة،
فما الذي لا يمكنني فعله باستخدام المصفوفة؟
أن أجعلها أكبر، صحيح؟
لا يمكنني إضافة عنصر إليها دون نسخ كل عنصر،
كما ناقشنا ذلك حتى الآن اليوم.
ولكن ماذا لو كنت أذكى قليلاً بشأن ذلك؟
ماذا لو قمت بتخزين قيمي، ليس فقط في مصفوفة،
ولكن بدأت في تخزينها في تلك الدوائر--
دعونا نطلق عليها عُقد--
وكل واحدة من تلك العُقد هي في الواقع مجرد عدد صحيح بالإضافة إلى قيمتين إضافيتين؟
كيف لنا أن ننفذ بنية البيانات تلك في الذاكرة؟
حسنًا، إليك int int-- يمكنها أن تمثل الرقم المعني.
ويمكننا وضع ذلك الرقم في بنية بيانات
التي تسمى عقدة وهي فقط تحتوي على الصيغة نفسها كما وضحنا في وقت سابق اليوم،
لكني تركت مساحة لحقلين آخرين.
ما الذي أرغب في تمثيله في تعليمة برمجية إذا
أردت البدء في تخزين أرقامي، ليس في مصفوفة المدرسة القديمة تلك من الأسبوع 0،
ولكن في شجرة؟
الجمهور: مؤشران.
ديفيد ج. مالان: اثنان--
الجمهور: مؤشران.
ديفيد ج. مالان: مؤشران.
حسنًا؟
شجرة، كما هو مرسوم هنا حرفيًا باستخدام الأسهم،
هو فقط مثل قول كل واحدة من تلك العقد أو الدوائر
تحتوي على طفل في جهة اليسار وطفل في جهة اليمنى.
كيف تقوم بتنفيذ الأطفال؟
حسنًا، يمكنك حرفيًا استخدام تدوين المؤشر فقط أيضًا هنا.
الطفل على الجهة اليسرى هو مجرد مؤشر إلى بنية أخرى على اليسار.
والطفل على الجهة اليمنى هو مجرد مؤشر آخر للطفل على اليمين.
والأمر الجيد في ذلك في النهاية أنه يمكننا الآن
اجتياز تلك الشجرة بقدر من الكفاءة التي تمكننا من اجتياز تلك المصفوفة.
لأنه لاحظوا أنني إذا أردت البحث عن الرقم 66،
فكم عدد الخطوات التي سأقوم بها إذا بدأت من الأعلى؟
فقط كما كانت كومي تمثل بداية القائمة المرتبطة الخاصة بنا،
لذا في عالم الشجرة هل للجذور أهمية خاصة.
وهذا هو المكان الذي منه نبدأ دائمًا.
إذن كم عدد الخطوات لأعثر على 66 المُعطى في الأعلى؟
الجمهور: ثلاث خطوات.
الجمهور: خطوتان.
ديفيد ج. مالان: يبدو--
أجل، خطوتان أو ثلاث خطوات، أليس كذلك؟
سأبدأ من الأعلى.
أنظر إليه وأقول، امم، 55، أي طريق سأسلك.
سأنتقل إلى اليمين.
وبعد ذلك أرى 77.
حسنًا.
أي طريقٍ سأسلك؟
سأنتقل إلى اليسار.
إذن إنه المنطق نفسه كما في الأسبوع 0 في خوارزمية فرق تسد التي استخدمناها في دليل الهاتف
أو في المصفوفة بعد أسبوعين من ذلك.
لكننا ننتقل إلى الرقم الذي نهتم به بسرعة كبيرة.
وهو ليس خطيًا.
وفي الواقع، إذا أجرينا بالفعل العملية الحسابية، ما الذي يُعد
رائعًا حقًا بشأن شجرة البحث الثنائي هو إذا كانت لديك عناصر n،
دوائر n، فارتفاع تلك الشجرة حسب التعريف وبشكل حسابي هو log n.
لذا ارتفاع الشجرة يتم فقط
ليتوافق بالضبط مع عدد المرات التي يمكن أن تأخذ فيها n،
وتقسّمها، تقسّمها، تقسّمها، تقسّمها إلى اثنين.
ويمكنك في الواقع أن ترى هذا إذا فكرت في ذلك الأمر بالاتجاه المعاكس.
في الصف السفلي، كم عدد العناصر الموجودة هناك؟
حسنًا؟
وفي الصف الأوسط، توجد؟
الجمهور: عنصران.
ديفيد مالان: عنصران.
وفي الصف العلوي، يوجد عنصر واحد.
إذن يمكنكم في الواقع رؤية ذلك في الاتجاه المعاكس.
هذا مثل خوارزمية فرق تسد، لكن بطريقة مختلفة من الناحية النظرية.
يحتوي كل صف في الشجرة على نصف عدد العناصر الموجود في الصف أدناه.
وبالتالي أثر هذا فقط مثل ما رأيناه في الأسبوع 0 في دليل الهاتف
عندما نقوم بالتقسيم، والتقسيم، والتقسيم إلى نصف، ونصف، ونصف.
إذن هذا فقط لأقول، الآن لدينا بنيات ومؤشرات،
يمكننا بناء شيء من هذا القبيل.
ولكن دعونا نجرب مثالاً آخر هنا أيضًا.
هذا يبدو مثالاً مجنونًا.
لكنه مدهش إلى حد ما.
لنفترض أنه، إذا أردنا تخزين قاموس من الكلمات--
ليست أسماء أشخاص هذه المرة، لكن من الكلمات الإنجليزية.
إذن ما الذي يحتوي عليه قاموس Merriam Webster أو قاموس Oxford الإنجليزي؟
الآلاف، مئات الآلاف من الكلمات
المتداولة هذه الأيام باللغة الإنجليزية على سبيل المثال؟
كيف تقوم بتخزين هذه الكلمات في الواقع؟
حسنًا، إذا كنت تبحث عن كلمات في القاموس مجددًا في السنة الماضية،
هذا خطي.
يجب عليك أن تبدأ من البداية وتنظر بداخله
صفحة بصفحة، بحثًا عن الكلمات.
أو قد تكون أكثر ذكاءً قليلاً.
لأن الكلمات في أي قاموس آمل، أن تكون مرتبة ترتيبًا أبجديًا،
يمكنك القيام بخوارزمية فرق تسد الخاصة بأسلوب محمد محمود عن طريق الانتقال إلى الوسط،
ثم وسط المنتصف، وهكذا--
سجل n.
لكن ماذا لو أخبرتكم، أنه يمكنكم البحث عن الكلمات في وقت ثابت--
عدد محدد من الخطوات؟
ولا شيء من هذا التعقيد المتعلق بخوارزمية فرق تسد.
لا log n.
فقط وقت ثابت-- تريد كلمة، اذهب واحصل عليها على الفور.
هنا حيث تظهر البنية الأخيرة، والتي يُطلق عليها trie--
T-R-I-E-- اختصار لكلمة استرجاع، على الرغم من كونها تنطق العكس.
إذن trie عبارة عن شجرة تعد كل عقدة منها مصفوفة.
لذا فإنها تشبه وحش فرانكشتاين الغريب الذي يمثل نوع من بنية البيانات.
نحن فقط نجمع العديد من الأفكار المختلفة بالفعل، على النحو التالي.
والطريقة التي تعمل بها trie، كما هو موضح في هذا الرسم البياني الجزئي على اللوحة،
هي إذا كنت تريد تخزين اسم براين، على سبيل المثال،
في قاموسك-- إنها الكلمة الأولى--
الأمر الذي ستقوم به هو البدء بإنشاء شجرة بعقدة واحدة فقط.
لكن تلك العقدة هي بفعالية مصفوفة.
هذه المصفوفة هي من الحجم، دعونا نقل للتبسيط، 26.
لذلك من أ إلى ي. وبالتالي يمثل هذا الموقع هنا ب لبراين.
لذا إذا أردت إدراج براين في هذه الشجرة، سأنشئ عقدة واحدة في الأعلى.
ثم للحرف الثاني في اسمه، ر،
سأُنشئ عقدة أخرى، مصفوفة أيضًا، من أ إلى ي.
ولذا هنا، سأضع مؤشر لهذه العقدة هنا.
ب-ر-ا. لذا يجب أن أرسم بعض المربعات الإضافية.
أ، ب، ت، ث، ج، ح، خ، د، ذ. لذا هنا، سأرسم مؤشر آخر إلى ب--
انتظروا.
Bian.
[LAUGHTER]
حسنًا.
هذا خطأ.
سيكون بيلي هو الاسم.
بيلي في ب. انتظروا.
لا.
اللعنة.
ب، ب. ب-ي-ا-- نعم، ينجح هذا الأمر.
هذا الأمر ينجح.
حسنًا.
معذرة.
ها نحن ذا.
نحن نقوم بإدراج بيلي في بنية البيانات الرائعة هذه.
لذلك تمثل العقدة الأولى الحرف الأول.
تمثل العقدة الثانية الحرف الثاني.
تمثل العقدة الثالثة الحرف الثالث.
وهكذا.
لكن الأمر الرائع في ذلك هو إمكانية إعادة الاستخدام.
إذن لاحظوا إذا كان ذلك هو الحرف الثانى وأنا قمت بالعد بشكل صحيح،
أنا، هذا سيقودنا بشكل أعمق إلى عقدة ثالثة
فى الشجرة حيث حرف ل الذي سنهتم به لاحقًا، ثم حرف آخر
فى الأسفل هنا الذي يمثل ل آخر.
وسأبدأ برسم الأحرف.
L. هذا B. هذا I. L. وسنسميها L.
ومن ثم، أخيرًا، واحدة أخرى هنا، وهي Y. وهذه
تشير إلى الأسفل هنا.
هذه تشير إلى هنا.
وهكذا.
إذن باختصار، نحن لدينا عقدة واحدة بشكل أساسي
لكل حرف فى الكلمة التي ندرجها فى بنية البيانات.
الآن، هذا يبدو غير فعّال بغباء في هذه اللحظة.
لأنه لتخزين ب، ي، ل، ي، كم حجم الذاكرة الذي استَخدمتُه؟
26 زائد 26 زائد 26 زائد 26 زائد 26.
فقط لتخزين خمسة أحرف، استخدمتُ 26 ضرب 5.
لكن هذا النوع من الموضوعية في علوم الكمبيوتر--
يشغل المزيد من المساحة قليلاً، وأراهن أنه ما يزال بإمكاني تقليل كمية الوقت
التي يستغرقها العثور على أي شخص.
لأنه لا يهم الآن عدد الطلاب الآخرين في بنية البيانات هذه--
وعلى سبيل المثال، دعونا نقوم بمثال واحد آخر.
لو حصلنا على اسم شخص آخر، مثل بوب--
إذن ب هو الحرف الأول نفسه.
هذا يقودنا إلى العقدة الثانية هذه.
O في مكان آخر في هذه المصفوفة، لنقل، هنا.
إذن هذا يمثل O. ومن ثم يحتوي بوب على حرف آخر.
إذن ستكون هنا مصفوفة أخرى.
وهذا هو السبب وراء توضيح الصورة الموجودة بالأعلى لهذا بشكل مختصر للغاية.
هذه هي الكيفية التي قد نُخزن اسم بوب بها.
إذن ب، ي، ل، ي. أو قد تتبع مسارًا مختلفًا، ب، و، ب.
إذن يمكننا أن نبدأ في إعادة استخدام بعض هذه المصفوفات.
إذن هنا حيث تبدأون في الحصول على بعض الفاعلية.
في أي وقت تتشارك الأسماء في بعض الأحرف، ثم تبدأون في إعادة استخدام نفس العُقد.
إذن هذا ليس إهدارًا كبيرًا للغاية.
لكن السؤال الآن هو، لو أنه يوجد 1000 طالب مثلاً في الصف الدراسي،
أو 1000 طالب في الغرفة، سيكون لدينا الكثير من العُقد
هناك على اللوحة.
لكن ما هو عدد الخطوات التي سيستغرقها العثور على بيلي،
أو بوب، أو أي اسم آخر في بنية البيانات تلك، ولمعرفة هل الطالب
موجود في الصف الدراسي نعم أم لا؟
لذا، مثلاً، خمسة لبيلي، ثلاثة لبوب.
ولاحظوا أنه لا توجد أي علاقة بين أي من هذه العمليات الحسابية
وعدد الطلاب الموجودين في الغرفة.
وبدلاً من ذلك، لو كتبنا قائمة طويلة من 1000 اسم، في أسوء الحالات،
قد يستغرق الأمر 1000 خطوة للعثور على بيلي أو بوب.
ربما يمكنني أن أكون أكثر ذكاء قليلاً إذا رتبتها.
لكن في أسوء الحالات، حرف O الكبير من n، هو خطي.
أو لو استخدمت جدول علامة التجزئة من قبل، وربما يوجد
1000 طالب فى الغرفة، لكن، حسنًا، هناك
26 حرفًا في الأبجدية الإنجليزية على الأقل.
إذن توجد 26 حزمة.
لذا ربما يكون 1000 مقسومًا على 26، في أسوأ الحالات،
إذا كنت أستخدم هذه القوائم المرتبطة داخل مصفوفتي.
لكن انتظروا دقيقة.
إذا كنت أستخدم هذه البنية، trie، حيث كل عقدة في الشجرة
هي فقط في مصفوفة تقودني إلى العقدة التالية، مثل الآثار، B، I، L، L،
Y هي 5 ودائمًا 5.
B، O، B دائمًا 3.
B، R، I، A، N سيكون 5 أيضًا.
ليس لأي من هذه الأرقام الإجمالية أي أثر أو أي تأثير
من الرقم الإجمالي للأسماء في بنية البيانات.
لذلك Trie بشكل منطقي هي هذه الكأس المقدسة المدهشة
في ذلك، من خلال جمع بنيات البيانات المختلفة هذه، والآن يمكنكم الحصول على وقت ثابت،
لكن ستدفعون ثمن ذلك.
وفقط للتوضيح، ما هو الثمن الذي يبدو أننا سندفعه؟
الجمهور: الذاكرة.
ديفيد ج. مالان: الذاكرة.
في الواقع، هذا هو السبب الذي جعلني لا أرسم المزيد بالفعل.
لأنها تصبح مجرد فوضى كبيرة على الشاشة لأنه
من الصعب رسم بنيات البيانات الكبيرة هذه.
إنها تأخذ كمية هائلة من الذاكرة.
لكن من الناحية النظرية، الأمر يحدث بشكل أسرع.
أجل؟
سؤال.
الجمهور: إذن هل تتعامل مع حالة إذا كان هناك شخص باسم بوب،
ولكن بعد ذلك هناك طفل آخر باسم بوبي؟
ديفيد ج. مالان: سؤال جيد.
إذن، هذا جزء من التبسيط.
إذا كنت تخزن كل من بوب وبوبي، فستستمر في الواقع.
لذا كل هذه العناصر ليست حرفًا واحدًا فقط.
لديك أيضًا بشكل أساسي عقدة هناك أو بعض بنيات البيانات الأخرى
التي تقول إما توقف هنا أو تابع.
وسترون هذا في الواقع في المشاكل التي سنقوم
باقتراحها عليكم كيف يمكنكم تمثيل هذه الفكرة إذا
اخترتم سلوك هذا الطريق.
بالفعل، التحدي الذي ينتظرنا هو في الواقع شيء من هذا القبيل.
ستقومون بتنفيذ المدقق الإملائي الخاص بكم.
وسنقدم لكم تعليمة برمجية لكي تبدأون هذه العملية.
وبالطبع، يقوم المدقق الإملائي هذه الأيام في مستندات Google
وMicrosoft Word فقط بتمييز الكلمات التي بها أخطاء إملائية باللون الأحمر.
لكن ما الذي يحدث؟
وكيف يمكن لـ Word أو مستندات Google
القيام بالتدقيق الإملائي للغتك الإنجليزية أو أيًا كانت اللغة بهذه السرعة؟
حسنا، لديها قاموس في الذاكرة، ربما مع عشرات الآلاف
أو مئات الآلاف من الكلمات.
وكل ما تفعلانه باستمرار هو، في كل مرة تكتب كلمة
وتضغط على مفتاح المسافة، أو النقطة، أو Enter
تبحث بسرعة عن تلك الكلمة الجديدة أو هذه الكلمات في قاموسها
وتقول، نعم أم لا، يجب أن أضع خط أحمر تحت هذه الكلمة.
ولذا ما سنقوم بفعله هو إعطاؤكم ملف نصي كبير، نص ASCII،
يحتوي على ألف ومئة كلمة.
وسيتعين عليكم أن تقرروا كيفية تحميل ذلك
في الذاكرة، ليس فقط بشكل صحيح، ولكن بطريقة جيدة التصميم.
وسنمنحكم أداة، إذا اخترتم استخدامها،
في تلك الأوقات التي تستغرقها تعليماتكم البرمجية.
كما أنها تحسب مقدار ذاكرة الوصول العشوائي التي تستخدمونها بالفعل.
لكن الأهداف الرئيسية لهذا الأسبوع وأسبوعنا الأخير في لغة C
هي تناول بعض هذه الكتل البنائية الأساسية،
مثل المصفوفات، والمؤشرات، والبنيات،
واتخاذ القرار بأنفسكم للكيفية التي تقومون بها بتجميعها بشكل مريح أكثر
معًا، إلى أي مدى تريدون أن تضبطوا تعليماتكم البرمجية بشكل دقيق وتتجاوزون مجرد
الحصول عليها صحيحة، ولإعطائكم أفضل إحساس كامن من التعليمة البرمجية الأساسية
التي قد كتبها الأشخاص منذ سنوات في المكتبات
لجعل البرمجة قابلة للتنفيذ، مثل سكراتش.
لأنه في غضون بضعة أسابيع، سنقوم بالترجمة إلى Python.
وسيتم ضم عشرات السطور من التعليمة البرمجية التي قمت بكتابتها الآن
إلى سطر واحد، سطرين،
لأننا سنحصل على المزيد من الميزات من هذه اللغات الحديثة،
والرائعة.
لكن آمل أنه سيكون لديكم تقدير لما سيكون في الواقع
أسفل هذا الغطاء.
لذا سأبقى هنا لبعض الوقت من أجل الأسئلة الفردية.
دعونا نتوقف هنا.
خذوا بطة في طريقكم إلى الخروج ولزملائكم كذلك.
وسأراكم في المرة القادمة.