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

ترتيب الشرائح في Golang: slices.Sort وSortFunc وعدة حقول

رتّب الشرائح في Go بـ slices.Sort وslices.SortFunc، ورتّب البنى حسب حقل واحد أو عدة حقول بـ cmp.Compare، وأبقِ العناصر المتساوية بترتيبها بالترتيب المستقر، واقرأ شيفرة sort.Slice الأقدم.

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

ترتيب الأنواع المدمجة

ترتّب slices.Sort أي شريحة نوع عنصرها قابل للترتيب: الأعداد الصحيحة والعشرية والنصوص. ترتّب في المكان ولا تعيد شيئًا.

المخرجات:

[3 7 19 42 88]
[Bob Carl alice lisa]
true
2 true

تُرتَّب النصوص حسب البايتات، فيأتي كل حرف ASCII كبير قبل كل حرف صغير. ونادرًا ما يكون هذا ما يتوقّعه المستخدم للأسماء. القسم التالي يصلح ذلك.

وصلت الحزمة slices في Go 1.21. وSort فيها ترتيب سريع مقاوم للأنماط (pattern-defeating quicksort): O(n log n)، في المكان، وغير مستقر.

ترتيب مخصّص بـ SortFunc

تأخذ slices.SortFunc دالة مقارنة func(a, b T) int. أعد رقمًا سالبًا عندما يجب أن يأتي a أولًا، ورقمًا موجبًا عندما يجب أن يأتي b أولًا، وصفرًا عندما يتساويان. والدالة المساعدة cmp.Compare تعيد ذلك بالضبط للأنواع القابلة للترتيب.

تحجز strings.ToLower داخل المقارنة نصًا جديدًا كلما احتوى مدخلها حرفًا كبيرًا، والمقارنة تُنفَّذ نحو n log n مرة. للشرائح الكبيرة احسب المفاتيح بالحروف الصغيرة مرة واحدة. وللأسماء في لغات أخرى (العلامات، وقواعد اللغة المحلية) استخدم golang.org/x/text/collate، وهي خارج المكتبة القياسية.

يجب أن تكون دالة المقارنة متّسقة: إذا قالت إن a يأتي قبل b، يجب أن تقول إن b يأتي بعد a. الدالة التي تكسر هذا (مثلًا بإعادة -1 كلما اختلفت قيمتان) تنتج شريحة مرتّبة خطأ دون أي خطأ ظاهر. طرح الأعداد الصحيحة (return a - b) يبدو أنيقًا لكنه يفيض مع القيم الكبيرة؛ استخدم cmp.Compare.

ترتيب البنى

تتلقّى دالة المقارنة العناصر، فترتيب البنى حسب حقل هو الاستدعاء نفسه:

الموظّفون ذوو الرواتب المتساوية قد يخرجون هنا بأي ترتيب، لأن SortFunc غير مستقرة. إذا احتجت ترتيبًا مضمونًا، فاكسر التعادل بحقول إضافية أو استخدم ترتيبًا مستقرًا.

الترتيب حسب عدة حقول

قارن الحقل الأهم أولًا وانتقل إلى التالي فقط عند التعادل. تعيد cmp.Or (Go 1.22) أول وسيط غير صفري، فيصير هذا سطرًا واحدًا:

المخرجات:

eng     150 Cy
eng     120 Ana
eng     120 Eve
sales    90 Bob
sales    90 Dee

تُقيَّم كل مقارنة حتى عندما تحسم الأولى، لأنها وسائط عادية. وهذا يكلّف قليلًا لمقارنات الحقول. أما عندما يكون كسر التعادل مكلفًا، فاكتب سلسلة if c := ...; c != 0 { return c } يدويًا.

الترتيب المستقر

يحافظ الترتيب المستقر على العناصر المتساوية في المقارنة بترتيبها الأصلي. هذا مهم عندما يكون للمدخل ترتيب ذو معنى أصلًا، مثل سجلات مرتّبة حسب الوقت تجمّعها الآن حسب المستخدم.

يطبع هذا [{ana 2} {ana 4} {bob 1} {bob 3} {bob 5}]: داخل كل مستخدم يبقى التسلسل الأصلي. الترتيب المستقر يؤدّي عملًا أكثر، فاستخدمه فقط عندما يهمّ ترتيب العناصر المتساوية.

ترتيب خريطة

لا ترتيب للخرائط. لعرض خريطة مرتّبة حسب المفتاح، رتّب مفاتيحها: slices.Sorted(maps.Keys(m)) (Go 1.23). وللترتيب حسب القيمة، رتّب المفاتيح بمقارنة تبحث عن القيم:

كسر التعادل بالكلمة مهم: من دونه ستُطبع chan وslice (كلتاهما 7) بترتيب مختلف من تشغيل إلى آخر، لأن المفاتيح تخرج من الخريطة بترتيب عشوائي. راجع الخرائط للمزيد.

الحزمة sort: sort.Slice وأخواتها

قبل Go 1.21 كان الترتيب يمرّ عبر الحزمة sort. ستراها في كثير من الشيفرات الموجودة:

sort.Ints(nums)
sort.Strings(names)
sort.Slice(staff, func(i, j int) bool {
	return staff[i].Salary < staff[j].Salary
})
sort.SliceStable(staff, func(i, j int) bool { ... })

فروق تستحق المعرفة:

sort.Sliceslices.SortFunc
ما تتلقّاه الدالةالفهرسين i, jالعنصرين a, b
ما تعيدهbool (هل i أصغر من j)int (سالب، صفر، موجب)
أمان الأنواعتأخذ any، وتستخدم الانعكاسعامة، مفحوصة وقت الترجمة
السرعةأبطأأسرع

خطأ شائع مع sort.Slice هو الإغلاق على شريحة غير الشريحة التي تُرتَّب، لأن دالة less تفهرس بالموضع. ولا يمكن أن يقع هذا الخطأ مع SortFunc لأنها تسلّمك العناصر.

النوع sort.Interface (Len وLess وSwap) هو الشكل الأقدم. وما زال الطريقة لترتيب بيانات ليست شريحة واحدة، مثل شريحتين متوازيتين يجب أن تتحرّكا معًا. ومنذ Go 1.22 تستدعي sort.Ints وsort.Strings وsort.Float64s ببساطة slices.Sort.

أخطاء شائعة

  • توقّع أن تعيد Sort الشريحة المرتّبة. ترتّب في المكان ولا تعيد شيئًا. استخدم slices.Sorted(slices.Values(s)) إذا أردت شريحة مرتّبة جديدة، أو slices.Clone أولًا.
  • افتراض أن العناصر المتساوية تحتفظ بترتيبها. صيغ Stable وحدها تعد بذلك.
  • الطرح للمقارنة. a - b يفيض. استخدم cmp.Compare.
  • ترتيب أعداد عشرية فيها NaN. يضع cmp.Compare قيمة NaN قبل كل قيمة أخرى، وهذا يبقي الترتيب متّسقًا. أما مقارنة a < b المكتوبة يدويًا فلا.

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

كيف ترتّب شريحة في Go؟

للأرقام والنصوص استدعِ slices.Sort(s) (Go 1.21). ترتّب في المكان تصاعديًا. ولأي شيء آخر، أو لترتيب مختلف، استخدم slices.SortFunc(s, func(a, b T) int { ... })، حيث تعيد الدالة رقمًا سالبًا إذا جاء a أولًا، وموجبًا إذا جاء b أولًا، وصفرًا إذا تساويا.

كيف أرتّب شريحة تنازليًا في Go؟

بدّل الوسيطين في المقارنة: slices.SortFunc(s, func(a, b int) int { return cmp.Compare(b, a) }). أو رتّب تصاعديًا ثم استدعِ slices.Reverse(s).

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

قارن الحقل الأول، ولا تنتقل إلى التالي إلا عند التساوي. تفعل cmp.Or (Go 1.22) ذلك بالضبط: return cmp.Or(cmp.Compare(a.Dept, b.Dept), cmp.Compare(b.Salary, a.Salary), strings.Compare(a.Name, b.Name)) تعيد أول نتيجة غير صفرية.

ما الفرق بين sort.Slice وslices.SortFunc؟

sort.Slice(s, func(i, j int) bool) هي الواجهة البرمجية الأقدم: تأخذ دالة less على الفهارس وتستخدم الانعكاس لتبديل العناصر. أما slices.SortFunc(s, func(a, b T) int) فعامة، ومفحوصة الأنواع، وتأخذ العناصر مباشرة، وأسرع. يجب أن تستخدم الشيفرة الجديدة الحزمة slices؛ وما زالت sort.Slice شائعة في الشيفرة المكتوبة قبل Go 1.21.

Coddy programming languages illustration

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

ابدأ الآن