محتويات الدورة

Go / المتقدم: السلامة والأداء وتصميم الخدمات / الدرس 15

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

استخدم معاملات الأنواع للخوارزميات المشتركة فعلا مع حفظ العمليات والملكية التي يسمح بها النوع.

وقت القراءة التقريبي: 4 دقيقةتاريخ مراجعة المحتوى
في هذه الصفحة

أهداف الدرس

  • شرح مجموعات الأنواع وحدود النوع الأساسي وcomparable.
  • تصميم دالة عامة بعمليات مبررة.
  • اختيار العمومية أو الواجهات دون محو الدلالات.

القيد يحدد العمليات المسموحة

قدمت Go 1.18 معاملات الأنواع. يسمي التصريح العام معاملات بقيود تصف الأنواع والعمليات. يفحص المترجم الجسم لكل الأنواع المقبولة لا أول نوع في الاستدعاء فقط. تمثل any اسما بديلا لـinterface{} ولا تمنح حسابا بنفسها.

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

حفظ نوع عددي مسمى

يقبل المثال الأول أنواعا شبيهة بـint ويجمع شريحة. يحقق Count المسمى ~int وتبقى النتيجة Count. يستنتج معامل النوع من الشريحة. القيم صغيرة؛ ولا تزيل العمومية التجاوز ولا تنشئ سياسة جمع مفحوص عالمية.

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

go جمع عام يحفظ نوعا معرفا
package main

import "fmt"

type IntLike interface{ ~int }
type Count int

func Sum[T IntLike](values []T) T {
	var total T
	for _, value := range values {
		total += value
	}
	return total
}

func main() {
	total := Sum([]Count{2, 3, 4})
	fmt.Printf("%T %v\n", total, total)
}
الناتج المتوقع
main.Count 9

أساس Count هو int فيستنتجه المترجم وينتج قيمة Count تسعة.

المساواة مطلب دلالي

يسمح comparable بـ== و!= ويناسب مفاتيح مجموعة عامة. يزيل المثال الثاني النصوص المتكررة ويحفظ أول ظهور. يستخدم خريطة للعضوية ويلحق بترتيب المدخل، فلا يدخل ترتيب الخريطة في الخرج.

لا يعني قابل المقارنة مساواة مفيدة لكل مجال. لا يساوي NaN نفسه، وقد تحمل واجهة قيمة ديناميكية غير قابلة للمقارنة فتسبب هلعا. منذ Go 1.20 تسمح قواعد تحقيق comparable ببعض معاملات أنواع الواجهات، لذلك تبقى قابلية المقارنة الديناميكية مسؤولية المستدعي.

go إزالة تكرار ثابتة بأول ظهور
package main

import "fmt"

func Unique[T comparable](input []T) []T {
	seen := make(map[T]struct{})
	output := make([]T, 0, len(input))
	for _, value := range input {
		if _, ok := seen[value]; ok {
			continue
		}
		seen[value] = struct{}{}
		output = append(output, value)
	}
	return output
}

func main() { fmt.Println(Unique([]string{"b", "a", "b", "c", "a"})) }
الناتج المتوقع
[b a c]

تستخدم العضوية الخريطة ويتبع الخرج ترتيب المدخل b ثم a ثم c.

اختيار التجريد الذي يحمل الوعد المناسب

استخدم واجهة سلوكية عندما تختلف التنفيذات ويحتاج المستهلك طرقا صغيرة مثل io.Reader. استخدم معاملات الأنواع عندما تحفظ الخوارزمية أو البنية علاقات أنواع ساكنة. قد يقبل النوع العام واجهات اعتمادا؛ فالخياران متكاملان لا موضتان متنافستان.

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

حفظ الصياغة العامة ضمن الأساس

تستخدم الدورة دوال وأنواعا عامة متاحة في Go 1.22. لا تفترض وجود كل ميزة بالمواصفة الحالية حينها؛ فقد توسع الإصدارات الصياغة. مثلا تصف ملاحظات Go 1.27 المراجعة طرقا عامة، وهي خارج الأساس، فتستخدم الأمثلة دوال عامة على مستوى الحزمة.

اختبر أنواعا ملموسة ومسماة وفراغا وحدودا دلالية. فشل ترجمة عملية ممنوعة ملاحظة تصميم مفيدة لكنه يبقى مقتطفا غير قابل للتشغيل. يعتمد الأداء على التنفيذ ولا تعد العمومية بسرعة تلقائية أو توليد شيفرة واحد.

الأخطاء الشائعة وإصلاحها

لا يجمع جسم عام T مقيد بـany.

لماذا يحدث؟
لا يضمن القيد عملية عددية.
كيف تصلحه؟
استخدم مجموعة عددية مبررة أو احقن السلوك.
كيف تتجنب تكراره؟
اشتق القيود من العمليات المطلوبة.

يفشل نوع int مسمى مع قيد int دقيق.

لماذا يحدث؟
تسمي المجموعة int لا الأنواع ذات أساسه.
كيف تصلحه؟
استخدم ~int عند قصد المجموعة الأوسع.
كيف تتجنب تكراره؟
اختبر نوع مجال معرفا لا الأنواع الجاهزة فقط.

تهلع مجموعة عامة مع عنصر واجهة.

لماذا يحدث؟
قيمته الديناميكية غير قابلة للمقارنة.
كيف تصلحه؟
قيد المجال أو تحقق من تمثيل موثق.
كيف تتجنب تكراره؟
لا تساو قبول الواجهة الساكن بقابليتها الديناميكية.

اختبر فهمك

1. لماذا تحفظ Unique ترتيب المدخل رغم الخريطة؟

اعرض الحل المشروح

لا تجتاز الدالة الخريطة. تفحص العضوية أثناء مسح المدخل بترتيبه وتلحق أول ظهور فقط. لذلك لا يهم ترتيب اجتياز الخريطة غير المحدد. يعتمد البرهان على قيم مستقرة ومساواة موثقة؛ والتعديل المتزامن مسألة أخرى.

2. تستنسخ دالة عامة []Record يحتوي خريطة. ما ضمان النسخ وما المشترك؟

اعرض الحل المشروح

تنشئ تخزين شريحة خارجيا مستقلا وتنسخ قيم Record. تبقى حقول الخرائط تشير للخرائط نفسها فتظهر التحديثات في الاثنين. قدم استدعاء استنساخ عنصر أو نسخا داخليا صريحا إذا لزم الاستقلال. تحفظ العمومية الأنواع لا ملكية متكررة تلقائية.

أهم ما تعلمته

  • تحدد القيود العمليات الصالحة لكل نوع مقبول.
  • لا يمحو حفظ النوع متطلبات المجال والملكية.
  • افحص ميزات العمومية المرتبطة بالإصدار مقابل أساس الوحدة.

المراجع الرسمية