Menu
flag Ar iconالعربيةdown icon

الشرائح Slice في Golang: append وmake وlen مقابل cap وخطأ المشاركة

الشرائح (slices) هي نوع القوائم اليومي في Go. تعلّم make وappend والنمو، وlen مقابل cap، وكيف يتشارك التقطيع المصفوفة الخلفية (والخطأ الذي يسبّبه ذلك)، وcopy، والحزمة slices، والشرائح ثنائية الأبعاد، والفرق بين nil والفارغة.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

الشريحة في مثال واحد

الشريحة (slice) نافذة قابلة للنمو على مصفوفة. تنشئها بحرفية أو بـ make، وتكبّرها بـ append، وتقرأ حجمها بـ len.

لا طول بين القوسين المربّعين في []string. هذا هو الفرق عن نوع المصفوفة مثل [4]string، الذي حجمه ثابت.

ما هي الشريحة

قيمة الشريحة ترويسة صغيرة من ثلاثة حقول:

  • مؤشر إلى عنصر في مصفوفة خلفية
  • طول: عدد العناصر التي تراها الشريحة
  • سعة: عدد العناصر الموجودة من ذلك المؤشر حتى نهاية المصفوفة الخلفية

نسخ شريحة (إسنادها، أو تمريرها إلى دالة) ينسخ هذه الترويسة فقط. والنسختان تشيران إلى العناصر نفسها. تأتي كل مفاجآت الشرائح تقريبًا من نسيان ذلك.

make: الطول والسعة

تحجز make([]T, len, cap) مصفوفة خلفية وتعيد شريحة عليها. السعة اختيارية وقيمتها الافتراضية تساوي الطول.

خلط شائع: make([]int, 5) متبوعة بخمسة استدعاءات append تعطيك عشرة عناصر، أولها خمسة أصفار. استخدم make([]int, 0, 5) عندما تنوي الإلحاق، أو make([]int, 5) عندما تنوي الإسناد بالفهرس.

الحجز المسبق للسعة عندما تعرف الحجم النهائي يتجنّب النمو المتكرّر. إنه تحسين، لا شرط.

append والنمو

تضيف append العناصر بعد الطول الحالي. إذا كانت السعة كافية تكتب في مكانها. وإلا تحجز مصفوفة أكبر، وتنسخ العناصر القديمة إليها، وتعيد شريحة تشير إلى المصفوفة الجديدة.

تتضاعف السعة تقريبًا ما دامت الشريحة صغيرة وتنمو بمعامل أصغر (يقترب من 1.25x) بعد تجاوزها 256 عنصرًا. الأرقام الدقيقة تفصيل في وقت التشغيل تغيّر بين إصدارات Go، فلا تكتب أبدًا شيفرة تعتمد عليها.

لأن append قد تعيد مصفوفة مختلفة، يجب أن تستخدم نتيجتها:

append(s, 4)     // compile error: append(s, 4) (value of type []int) is not used
s = append(s, 4) // correct

إلحاق شريحة كاملة يستخدم صيغة النشر: s = append(s, other...).

التقطيع يتشارك المصفوفة الخلفية

تنشئ s[low:high] ترويسة شريحة جديدة على المصفوفة نفسها، تغطّي الفهارس من low حتى high دون شموله. يمكن حذف أي من الحدّين. لا تُنسخ أي عناصر.

المخرجات:

[10 99 30 40 50]
3 4

هذه المشاركة هي ما يجعل التقطيع رخيصًا. وهي أيضًا مصدر أشهر خطأ في الشرائح.

خطأ التسمية المزدوجة مع append

عندما يكون لشريحة فرعية سعة فائضة، يكتب الإلحاق بها في مصفوفة الأب، فوق عناصر ما زال الأب يستخدمها.

المخرجات:

[1 2 100 4 5]
[1 2 100]
[cmd test] [cmd test]

تتشارك a وb خانة المصفوفة التي بعد "cmd"، فكتب الإلحاق الثاني فوق الأول. تبدو الشيفرة سليمة وتعمل كلما صادف أن السعة ممتلئة، ولهذا يظهر هذا الخطأ أحيانًا فقط.

إصلاحان:

  1. تعبير التقطيع الكامل s[low:high:max] يحدّ السعة بـ max-low. دون سعة فائضة يجب أن يحجز append التالي. تجعل first := base[:2:2] المثال الأول آمنًا.
  2. انسخ صراحة عندما ستعيش الشريحة أطول من الاستدعاء أو سيُلحق بها بشكل مستقل: slices.Clone(prefix) أو append([]string(nil), prefix...).

فخ مرتبط: شريحة صغيرة من مصفوفة ضخمة تُبقي المصفوفة كلها حيّة لجامع النفايات. إذا قرأت ملفًا بحجم 100 MB واحتفظت بـ data[:10]، تبقى الـ 100 MB في الذاكرة. انسخ الجزء الذي تحتفظ به.

copy

تنسخ الدالة المدمجة copy(dst, src) عدد min(len(dst), len(src)) من العناصر وتعيد ذلك العدد. ولا تكبّر dst أبدًا.

تتعامل copy بشكل صحيح مع تداخل المصدر والوجهة، فتقدّم copy(s[1:], s) العناصر موضعًا واحدًا دون إفساد.

الحزمة slices

منذ Go 1.21 تحتوي الحزمة القياسية slices على دوال مساعدة عامة للعمليات التي كنت تكتبها يدويًا.

الدالةما تفعله
Contains، Indexإيجاد قيمة (ContainsFunc وIndexFunc تأخذان دالة شرط)
Sort، SortFunc، SortStableFuncالترتيب في المكان
BinarySearchالبحث في شريحة مرتّبة
Insert، Delete، DeleteFuncالإدراج أو الحذف، وإعادة الشريحة الجديدة
Compactإزالة التكرارات المتتالية
Equal، Compareالمقارنة عنصرًا بعنصر
Clone، Reverse، Max، Minما تدلّ عليه أسماؤها
Collect، Sorted، Values، Allالعمل مع المكرِّرات (Go 1.23)

تعيد Delete وInsert شريحة يجب أن تسندها، مثل append. وللترتيب صفحته الخاصة: الترتيب.

حذف العناصر أثناء المرور بالفهرس يتخطّى عناصر. استخدم slices.DeleteFunc بدلًا من ذلك:

s = slices.DeleteFunc(s, func(n int) bool { return n%2 == 0 }) // drop evens

الشرائح ثنائية الأبعاد

شريحة من الشرائح تعطيك شبكة يمكن أن تختلف أطوال صفوفها. يجب حجز كل صف على حدة.

نسيان make الداخلية يترك كل صف nil، فتسبّب grid[1][2] = 7 حالة panic برسالة index out of range [2] with length 0.

الشرائح nil والشرائح الفارغة

تتصرّف الشريحة nil كالفارغة مع len وcap وrange وappend، ففضّل var s []T كحالة صفرية. هيّئ بـ []T{} فقط عندما يهمّ الفرق، وهذا في الغالب مخرجات JSON (null مقابل []). افحص الفراغ بـ len(s) == 0، لا بـ s == nil.

الشرائح والدوال

تستطيع الدالة التي تتلقّى شريحة تغيير عناصرها، ويرى المستدعي التغييرات. لكنها لا تستطيع تغيير طول المستدعي، لأنها تلقّت نسخة من الترويسة. الدالة التي تلحق يجب أن تعيد الشريحة الجديدة:

func addAll(s []int, vals ...int) []int {
	return append(s, vals...)
}

لهذا تعيد append وslices.Delete وslices.Insert كلها شريحة.

أخطاء شائعة

  • تجاهل نتيجة append. لا تُترجم عند إهمالها كليًا، لكن إسناد append(s, x) إلى متغيّر غير الذي تواصل استخدامه خطأ منطقي.
  • إلحاقان من أساس واحد. قد يتشاركان التخزين. انسخ الأساس أو استخدم تعبير تقطيع كاملًا.
  • make([]T, n) ثم append. تترك n صفرًا في البداية.
  • فهرس خارج النطاق. تسبّب s[len(s)] حالة panic. العنصر الأخير هو s[len(s)-1].
  • مقارنة الشرائح بـ ==. لا تُترجم إلا s == nil. استخدم slices.Equal.

الأسئلة الشائعة

ما الفرق بين طول الشريحة وسعتها في Go؟

len(s) هو عدد العناصر التي تحملها الشريحة حاليًا. وcap(s) هو عدد العناصر التي تتّسع لها المصفوفة الخلفية بدءًا من أول عنصر في الشريحة. تكتب append في السعة الفائضة عندما توجد، وتحجز مصفوفة جديدة أكبر عندما لا توجد.

كيف تلحق عناصر بشريحة في Go؟

استدعِ الدالة المدمجة append وأسند النتيجة: s = append(s, x). يمكنها إضافة عدة قيم (append(s, 1, 2, 3)) أو شريحة أخرى (append(s, other...)). استخدم القيمة المعادة دائمًا، لأن append قد تعيد شريحة تشير إلى مصفوفة جديدة.

كيف أحذف عنصرًا من شريحة في Go؟

استخدم slices.Delete(s, i, i+1) (Go 1.21)، التي تقدّم العناصر اللاحقة موضعًا واحدًا وتعيد الشريحة الأقصر. ومنذ Go 1.22 تصفّر أيضًا الخانات المحرّرة في النهاية. وإذا لم يكن الترتيب مهمًا، فبدّل مع العنصر الأخير واقتطع: s[i] = s[len(s)-1]; s = s[:len(s)-1].

كيف أتحقّق من احتواء شريحة على قيمة في Go؟

استخدم slices.Contains(s, v) من الحزمة القياسية slices (Go 1.21). تعيد slices.Index(s, v) الموضع أو -1، وتأخذ slices.ContainsFunc دالة شرط. وللبحث المتكرّر في بيانات كبيرة ابنِ خريطة بدلًا من ذلك.

ما الفرق بين الشريحة nil والشريحة الفارغة في Go؟

var s []int قيمتها nil؛ وs := []int{} فارغة لكنها ليست nil. طول كلتيهما 0، وكلتاهما تعملان مع len وrange وappend. يظهر الفرق في فحوص s == nil وفي encoding/json، التي ترمّز الشريحة nil كـ null والفارغة كـ [].

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن