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

المعامل الثلاثي Ternary في Golang: لماذا لا تملكه Go وماذا تستخدم

لا تملك Go معاملًا ثلاثيًا (cond ? a : b). تشرح هذه الصفحة السبب، وتعرض البديل المعتاد بـ if/else، وcmp.Or للقيم الافتراضية، ودالة مساعدة عامة مع الفخ الذي يجعلها تختلف عن معامل ثلاثي حقيقي.

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

لا تملك Go معاملًا ثلاثيًا. لا يوجد cond ? a : b. البديل المعتاد عبارة if:

عرّف المتغيّر بالقيمة الافتراضية، ثم غيّره في if. وعندما يحتاج الفرعان عملًا، استخدم if/else:

var fee int
if member {
	fee = 0
} else {
	fee = computeFee(order)
}

لماذا تركته Go

تجيب الأسئلة الشائعة لـ Go عن هذا مباشرة: رأى المصمّمون المعامل ?: يُستخدم كثيرًا لصنع تعابير معقّدة يتعذّر فهمها، وقرّروا أن if/else، وإن كانت أطول، أوضح بلا جدال. واللغة لا تحتاج إلا بنية تحكّم شرطية واحدة.

عمليًا يكلّف غياب المعامل سطرين أو ثلاثة لكل استخدام، ويزيل فئة كاملة من التعابير المتداخلة مثل a ? b ? c : d : e.

العودة المبكرة بدلًا من ذلك

داخل دالة، يتخطّى الشكل الأنظف المتغيّر كليًا في الغالب:

نقل الشرط إلى دالة مسمّاة صغيرة هو الجواب الأفضل غالبًا: يُقرأ موضع الاستدعاء كالمعامل الثلاثي الذي أردته، ويوثّق اسم الدالة معنى الشرط.

القيم الافتراضية بـ cmp.Or (Go 1.22)

أشيع استخدام للمعامل الثلاثي في لغات أخرى هو "استخدم هذه القيمة، أو بديلًا إن كانت فارغة": name ? name : "anonymous"، أو name || "anonymous" في JavaScript. أضافت Go 1.22 الدالة cmp.Or لهذا بالضبط. تعيد أول وسيط ليس القيمة الصفرية لنوعه:

تعامل القيمة الصفرية على أنها "مفقودة"، فلا تستطيع التمييز بين "المنفذ 0 عمدًا" و"المنفذ لم يُضبط". عندما يكون الصفر قيمة صالحة، استخدم مؤشرًا، أو bool منفصلًا، أو if صريحة.

دالة If مساعدة عامة، وفخّها

مع الأنواع العامة (Go 1.18) يمكنك كتابة دالة تشبه المعامل الثلاثي:

func If[T any](cond bool, a, b T) T {
	if cond {
		return a
	}
	return b
}

status := If(ok, "pass", "fail")

تعمل مع القيم البسيطة، وتستخدمها بعض قواعد الشيفرة. لكنها ليست معاملًا ثلاثيًا، والفرق مهم. تقيّم Go كل الوسائط قبل استدعاء الدالة، فيُحسب a وb دائمًا:

يحسب الاستدعاء الأول "a" و"b" كليهما، ويسبّب الثاني panic بسبب إلغاء مرجعية مؤشر nil، رغم أن الشرط خاطئ. أما المعامل الثلاثي الحقيقي فكان سيقيّم الطرف المختار وحده. لذلك:

  • لا تستخدم هذه الدالة المساعدة أبدًا عندما يلغي أحد الطرفين مرجعية مؤشر، أو يفهرس شريحة، أو يقرأ مدخل خريطة قد لا يوجد.
  • لا تستخدمها أبدًا عندما يكون أحد الطرفين مكلفًا أو له آثار جانبية.
  • للقيم الرخيصة والآمنة تعمل، لكن كثيرًا من مراجعي Go سيطلبون مع ذلك if العادية.

يمكنك جعل الفرعين كسولين بتمرير دوال، If(ok, func() string { return a }, func() string { return b })، لكن عندها تصير عبارة if أقصر.

حيل أخرى، ولماذا تتجنّبها

البحث في خريطة. map[bool]string{true: "yes", false: "no"}[ok] تعمل، وتبني خريطة وتحسب تجزئتها في كل تقييم، وتقيّم القيمتين. إنها طرفة، لا أسلوب معتمد.

دالة تُستدعى فورًا. الدالة المجهولة المستدعاة في مكانها تعطيك تعبيرًا:

label := func() string {
	if n > 0 {
		return "positive"
	}
	return "non-positive"
}()

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

اختيار البديل

الحالةاستخدم
الاختيار بين قيمتينقيمة افتراضية، ثم if
الفرعان يحتاجان حسابًاif/else
القيمة تحدّد نتيجة الدالةreturn مبكرة
بديل عندما تكون القيمة فارغةcmp.Or(value, fallback)
ثلاث حالات أو أكثرswitch
شرط متكرّر له معنىدالة مسمّاة صغيرة

تظهر مقترحات إضافة تعبير شرطي بانتظام في متتبّع مشكلات Go، وقد رُفضت حتى الآن. اكتب if.

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

هل تملك Go معاملًا ثلاثيًا (ternary)؟

لا. لا تملك Go التعبير cond ? a : b. تقول الأسئلة الشائعة لـ Go إن المصمّمين تركوه لأنه يُستخدم كثيرًا لبناء تعابير صعبة القراءة. استخدم عبارة if/else بدلًا منه.

كيف أكتب إسنادًا شرطيًا في سطر واحد في Go؟

أسند القيمة الافتراضية أولًا، ثم غيّرها:

label := "odd"
if n%2 == 0 {
	label = "even"
}

هذا هو البديل المعتاد. لن يضع gofmt جسم if في سطر واحد، فيأخذ دائمًا ثلاثة أسطر على الأقل.

هل يمكنني كتابة دالة ternary عامة في Go؟

نعم: func If[T any](cond bool, a, b T) T { if cond { return a }; return b }. لكن المشكلة أن Go تقيّم كل الوسائط قبل استدعاء الدالة، فيُحسب a وb كلاهما. ما زالت If(p != nil, p.Name, "") تلغي مرجعية p قيمتها nil وتسبّب panic. أما المعامل الثلاثي الحقيقي فيقيّم طرفًا واحدًا فقط.

كيف أضبط قيمة افتراضية إذا كان النص فارغًا في Go؟

منذ Go 1.22 تعيد cmp.Or(name, "anonymous") أول وسيط ليس القيمة الصفرية، فتعطي name ما لم يكن name مساويًا لـ "". تعمل مع أي نوع قابل للمقارنة، ومثل أي استدعاء دالة تقيّم كل وسائطها.

Coddy programming languages illustration

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

ابدأ الآن