Stack<T> كومة: تضع العناصر في القمة بـ Push وتأخذها من القمة بـ Pop، فيكون آخر عنصر يدخل أول عنصر يخرج (LIFO). لا يمكن الوصول إلا إلى القمة، وكل عملية عليها تستغرق زمنًا ثابتًا.
Push وPop وPeek
المخرجات:
On top: green
Count: 3
Took green
On top: red
Took red
Took blue
Count: 0
دُفع green أخيرًا، فيخرج أولًا. تعيد Peek القمة دون تغيير المكدّس، وهكذا تفحص ما ستعطيك إياه Pop قبل أن تقرّر أخذه.
استثناء المكدّس الفارغ وTryPop
السحب أو النظر في مكدّس فارغ يرمي InvalidOperationException. يظهر هذا غالبًا في المحلّلات والخوارزميات التي تتغذى بمدخلات فيها عناصر إغلاق أكثر من عناصر الفتح.
المخرجات:
Caught InvalidOperationException
True 10
False 0
تعيد TryPop وTryPeek (.NET Core 2.0 وما بعده) القيمة false على مكدّس فارغ وتضبطان متغيّر out على القيمة الافتراضية، 0 هنا. وفي .NET Framework افحص Count > 0 أولًا.
ترتيب المرور: القمة أولًا
تعداد المكدّس لا يحذف شيئًا، ويسير من القمة نزولًا، بالترتيب الذي ستعيد به Pop العناصر:
المخرجات:
checkout products home
checkout > products > home
True
home
checkout
النسخة المعكوسة توقع الناس: يأخذ المُنشئ أي IEnumerable<T> ويدفع عناصره بالترتيب، والمكدّس يُعدَّد من القمة أولًا، فتنتهي القمة القديمة في قاع النسخة. عكس التسلسل أولًا (تعيد Reverse() في LINQ العناصر من القاع أولًا) يعطي نسخة بالقمة نفسها.
دفع قائمة عناصر إلى مكدّس جديد يعكسها أيضًا، وهي طريقة سريعة لعكس تسلسل: تسحب new Stack<char>("hello") القيم o, l, l, e, h.
مثال: سجل التراجع
تحفظ المحرّرات كل تغيير في مكدّس. يسحب التراجع أحدث تغيير ويعكسه؛ ويحفظ الإعادة مكدّسًا ثانيًا للتغييرات المتراجع عنها.
المخرجات:
Hello, world!
Hello, world
Hello
Hello, world
تخزين لقطات كاملة هو النسخة الأبسط. أما المحرّرات الحقيقية فتدفع كائنات أوامر صغيرة بدلًا من ذلك (ما أُدرج، وأين)، لكل منها دالة تعكس نفسها، لكن المكدّسين يعملان بالطريقة نفسها.
مثال: الأقواس المتوازنة
التحقق من إغلاق ( و[ و{ بالترتيب الصحيح هو التمرين القياسي على المكدّس، والمنطق نفسه موجود داخل كل مترجم ومحلّل JSON.
المخرجات:
"f(a[i], {x: 1})" -> True
"(]" -> False
"((a)" -> False
"a)b(" -> False
"" -> True
تقابل فحوص الفشل الثلاثة الطرق الثلاث التي تخطئ بها الأقواس: قوس إغلاق دون شيء مفتوح (a)b(، يلتقطه Count == 0 بدل استثناء من Pop)، وقوس إغلاق من النوع الخطأ ((])، وأقواس فتح لم تُغلق أبدًا (((a)، يلتقطها الفحص الأخير).
استخدامات أخرى
- البحث بالعمق أولًا. استبدل الطابور في البحث بالعرض أولًا بمكدّس فيتعمّق المرور قبل أن يتوسّع. ويحلّ المكدّس الصريح أيضًا محل الاستدعاء الذاتي حين تكون المدخلات عميقة بما يكفي لخطر
StackOverflowException، الذي لا يمكن التقاطه. - تقييم التعابير. يُقيَّم الترميز اللاحق (
3 4 + 2 *) بدفع الأرقام وسحب اثنين لكل معامل. - التراجع (backtracking). سجل التصفح وحل المتاهات وحالات المحلّل تدفع موضعًا وتعود إليه بالسحب عند طريق مسدود.
راجع Queue لنظيره الداخل أولًا يخرج أولًا.
Stack مقابل Queue مقابل List
Stack<T> | Queue<T> | List<T> | |
|---|---|---|---|
| ترتيب الخروج | الأحدث أولًا | الأقدم أولًا | أي ترتيب، بالفهرس |
| الإضافة | Push | Enqueue | Add، Insert |
| الحذف | Pop (القمة) | Dequeue (المقدمة) | Remove، RemoveAt |
| النظر | Peek | Peek | list[i] |
| الأشكال الآمنة | TryPop، TryPeek | TryDequeue، TryPeek | غير لازمة |
ولعدة خيوط يقدّم ConcurrentStack<T> في System.Collections.Concurrent الدوال Push وTryPop وTryPeek دون أقفال.
أخطاء شائعة
- السحب دون فحص. المكدّس الفارغ يرمي
InvalidOperationException؛ افحصCountأو استخدمTryPop. - توقّع أن تبدأ
foreachمن أول عنصر دُفع. تبدأ من القمة. - النسخ بـ
new Stack<T>(stack). النسخة معكوسة. - الدفع داخل
foreachعلى المكدّس نفسه. يرمي استثناءً؛ استخدم حلقةwhile (stack.Count > 0).
الأسئلة الشائعة
ما هو Stack في C#؟
Stack<T> في System.Collections.Generic مجموعة الداخل أخيرًا يخرج أولًا (LIFO). تضع Push عنصرًا في القمة، وتحذف Pop عنصر القمة وتعيده، وتعيد Peek عنصر القمة دون حذفه. الثلاث تعمل في زمن ثابت.
ماذا يحدث حين تستدعي Pop على مكدّس فارغ في C#؟
ترمي Pop وPeek استثناء InvalidOperationException حين يكون المكدّس فارغًا. افحص stack.Count > 0 أولًا، أو استخدم TryPop(out var item) وTryPeek(out var item)، اللتين تعيدان false بدل رمي استثناء (.NET Core 2.0 وما بعده).
بأي ترتيب تمرّ foreach على Stack؟
من القمة نزولًا: آخر عنصر دُفع يأتي أولًا، بالترتيب نفسه الذي ستعيدها به Pop. وتستخدم ToArray() الترتيب نفسه. ومن نتائج ذلك أن new Stack<T>(otherStack) ينتج نسخة معكوسة، لأن المُنشئ يدفع العناصر بترتيب تعداده لها.
ما الفرق بين Stack وQueue في C#؟
يعيد Stack<T> أحدث عنصر أولًا (الداخل أخيرًا يخرج أولًا)، بينما يعيد Queue<T> أقدم عنصر أولًا (الداخل أولًا يخرج أولًا). استخدم المكدّس لسجل التراجع والبنى المتداخلة والبحث بالعمق أولًا؛ واستخدم الطابور لمعالجة العمل بترتيب الوصول والبحث بالعرض أولًا.
كيف أفحص توازن الأقواس في C#؟
امسح النص مرة واحدة. ادفع كل قوس فتح إلى Stack<char>. ولكل قوس إغلاق يجب ألا يكون المكدّس فارغًا ويجب أن تكون قمته قوس الفتح المقابل، الذي تسحبه بعد ذلك. يكون النص متوازنًا حين ينتهي المسح والمكدّس فارغ.