المستوى: مبتدئ — هذا المقال مكتوب لمن يعرف أساسيات بايثون (المتغيرات والحلقات) ولا يحتاج خبرة سابقة. كل مفهوم مشروح بمثال أولًا ثم بشكل علمي دقيق.
ليه بناء نص حرف-حرف ممكن يبطّئ كودك 42 مرة؟
لو بتبني نص كبير جوّه حلقة وبتضيف عليه حرف أو سطر في كل لفة، انت غالبًا بتنسخ النص كله من الأول في كل خطوة من غير ما تحس. في تجربة قِستها بنفسي على بايثون، بناء نص فيه 100 ألف حرف بالطريقة دي أخد 122 مللي ثانية، والطريقة الصح أخدت 2.9 مللي ثانية بس — أسرع حوالي 42 مرة. السبب مفهوم واحد اسمه عدم قابلية النص للتغيير (Immutability).
المشكلة باختصار
في بايثون، النص (str) كائن غير قابل للتعديل. يعني لما تكتب s = s + "x"، بايثون مش بيضيف حرف على النص القديم. هو بيعمل نص جديد تمامًا فيه المحتوى القديم + الحرف الجديد، وبيرمي القديم. لو ده حصل مرة، مش مشكلة. لو حصل 100 ألف مرة جوّه حلقة، انت بتعمل 100 ألف نسخة، وكل نسخة أكبر من اللي قبلها.
المفهوم بمثال بسيط الأول
تخيّل إنك بتكتب في دفتر بقلم حبر مايتمسحش. عايز تزوّد كلمة في سطر مكتوب؟ مفيش "تعديل". لازم تجيب صفحة جديدة وتنسخ السطر كله من الأول وتضيف الكلمة في الآخر. لو السطر كلمتين، النسخ سريع. لكن لو بقى صفحة كاملة، كل "تعديل" بسيط بيكلّفك إعادة نسخ الصفحة بالكامل.
النص في بايثون زي الدفتر بالحبر بالظبط. كل "تعديل" = صفحة جديدة + نسخ كامل. ده اللي بيخلي الشغل يتراكم.
المفهوم بشكل علمي ودقيق
أي كائن في بايثون له عنوان في الذاكرة تقدر تشوفه بدالة id(). جرّب الكود ده:
s = "hi"
print(id(s)) # مثال: 140278... (عنوان النص الأصلي)
s = s + "!" # ظاهريًا "تعديل"، لكنه إنشاء كائن جديد
print(id(s)) # عنوان مختلف تمامًا — ده نص تاني خالص
العنوان بيتغير لأن s + "!" أنتج كائنًا جديدًا. النص القديم "hi" فضل زي ما هو في مكانه لحد ما يتنضّف. ده معنى Immutability علميًا: حالة الكائن مابتتغيرش بعد إنشائه؛ أي عملية "تعديل" بترجّع كائنًا جديدًا.
دلوقتي طبّق ده على حلقة. لو بنيت نص طوله n بإضافة حرف في كل خطوة، الخطوة رقم k بتنسخ نص طوله k. مجموع النسخ = 1 + 2 + 3 + ... + n، واللي بيساوي تقريبًا n²/2. ده اللي بنسمّيه التعقيد التربيعي O(n²): ضِعف حجم البيانات يخلي الزمن يتضاعف أربع مرات، مش مرتين.
الفخ والحل بكود شغّال
ده الفخ (بناء النص من بدايته في كل لفة، وهو أوضح حالة O(n²)):