"أين دالة contains؟" — أول صدمة للمبتدئ في Go
\n\nتخيل أنك تكتب كودًا بلغة Go، ولديك مصفوفة من النصوص ["python", "go", "rust", "javascript"]، وتريد التحقق مما إذا كانت تحتوي على اللغة "go". إذا كنت قادمًا من Python أو Ruby أو JavaScript، فستكتب تلقائيًا slice.contains("go") — وستصدم بخطأ أحمر في الترجمة. "ما هذا الهراء؟" — ستتساءل. "أي لغة محترمة لديها دالة contains للمجموعات!"
اهدأ، فقط اهدأ. في Go لا توجد دالة contains مدمجة للمصفوفات. وهذا ليس خطأ، بل ميزة. قرر مطورو اللغة أن دالة واحدة لا يمكنها تغطية جميع الحالات (مقارنة النصوص، الأرقام، الهياكل)، وتركوا التنفيذ للمبرمج. قد يبدو الأمر مخيفًا، لكنه في الواقع بسيط وأنيق. في هذا المقال، سنشرح كيفية كتابة contains لمصفوفة في Go، مع تحليل الأداء، والأخطاء الشائعة، وحلول جاهزة.
\n\nلماذا لا توجد دالة contains للمصفوفات في Go؟
\nGo لغة بسيطة. لا تحتوي على الأدوية (قبل الإصدار 1.18) أو تحميل الدوال. المصفوفة ليست كائنًا بطرق، بل هي بنية بيانات: مؤشر لمصفوفة، طول، وسعة. قرر مطورو Go أن دالة contains يجب أن تكون صريحة، حتى يفهم المبرمج كيفية المقارنة بالضبط. هذا يقلل الأخطاء ويجعل الكود قابلًا للتوقع.
\nعلاوة على ذلك، دالة contains للمصفوفة هي دائمًا بحث خطي (O(n))، وإذا كنت تتوقع O(1)، فأنت تستخدم بنية بيانات خاطئة (تحتاج إلى map). Go لا يريد إخفاء هذا التعقيد.
\n\nأبسط دالة contains لمصفوفة في Go (البحث اليدوي)
\nلنكتب دالة contains لمصفوفة نصوص. هذا هو الكلاسيكي الذي ستراه في أي كود إنتاجي:
\n\npackage main\n\nimport "fmt"\n\n// Contains تتحقق من وجود عنصر في مصفوفة نصوص\nfunc Contains(slice []string, target string) bool {\n for _, item := range slice {\n if item == target {\n return true\n }\n }\n return false\n}\n\nfunc main() {\n languages := []string{"python", "go", "rust", "javascript"}\n fmt.Println(Contains(languages, "go")) // true\n fmt.Println(Contains(languages, "csharp")) // false\n}\n\nالأمر بسيط: نمر على المصفوفة بحلقة، ونقارن كل عنصر بالعنصر المطلوب. إذا وجدناه — true، وإلا false. مهم: العامل == يعمل مع النصوص، الأرقام، القيم المنطقية، والمؤشرات. لا يعمل مع الهياكل أو المصفوفات المتداخلة.
دالة contains للأرقام
\nلمصفوفة الأعداد الصحيحة، الكود مشابه جدًا:
\n\nfunc ContainsInt(slice []int, target int) bool {\n for _, v := range slice {\n if v == target {\n return true\n }\n }\n return false\n}\n\nfunc main() {\n numbers := []int{10, 20, 30, 40, 50}\n fmt.Println(ContainsInt(numbers, 30)) // true\n fmt.Println(ContainsInt(numbers, 99)) // false\n}\n\nغير مريح؟ نعم. لكن هذه هي Go — الوضوح والبساطة فوق كل شيء.
\n\nدالة contains عامة لمصفوفة في Go (الأدوية، Go 1.18+)
\nمع إصدار Go 1.18، ظهرت الأدوية (Generics). الآن يمكنك كتابة دالة contains واحدة لأي نوع يدعم المقارنة:
\n\npackage main\n\nimport "fmt"\n\n// ContainsGeneric تعمل مع أي نوع comparable\nfunc ContainsGeneric[T comparable](slice []T, target T) bool {\n for _, item := range slice {\n if item == target {\n return true\n }\n }\n return false\n}\n\nfunc main() {\n strs := []string{"a", "b", "c"}\n ints := []int{1, 2, 3}\n floats := []float64{1.1, 2.2, 3.3}\n\n fmt.Println(ContainsGeneric(strs, "b")) // true\n fmt.Println(ContainsGeneric(ints, 5)) // false\n fmt.Println(ContainsGeneric(floats, 2.2)) // true\n}\n\nالقيود comparable تعني أن النوع يدعم العوامل == و