سؤال

أنا شخصياً من المدافعين عن المشغل الثلاثي:() ؟:;أنا أدرك أن لها مكانها، لكني قابلت العديد من المبرمجين الذين يعارضون استخدامها تمامًا، وبعضهم يستخدمها كثيرًا.

ما هي مشاعرك تجاهه؟ما هو الكود المثير للاهتمام الذي رأيته وهو يستخدمه؟

هل كانت مفيدة؟

المحلول

استخدمه ل تعبيرات بسيطة فقط:

int a = (b > 10) ? c : d;

لا تسلسل أو عش المشغلين الثلاثية لأنه من الصعب القراءة والارتباك:

int a = b > 10 ? c < 20 ? 50 : 80 : e == 2 ? 4 : 8;

علاوة على ذلك ، عند استخدام المشغل الثلاثية ، فكر في تنسيق الكود بطريقة تحسن قابلية القراءة:

int a = (b > 10) ? some_value                 
                 : another_value;

نصائح أخرى

يجعل تصحيح الأخطاء أكثر صعوبة قليلاً حيث لا يمكنك وضع نقاط توقف على كل من التعبيرات الفرعية. أنا نادرا ما.

أنا أحبهم ، خاصة في اللغات الآمنة من النوع.

لا أرى كيف:

int count = (condition) ? 1 : 0;

هو أصعب من هذا:

int count;

if (condition)
{
  count = 1;
} 
else
{
  count = 0;
}

تعديل -

أود أن أزعم أن المشغلين الثلاثية يجعلون كل شيء أقل تعقيدًا وأكثر أناقة من البديل.

بالسلاسل أنا بخير - متداخلة ، وليس كثيرا.

أميل إلى استخدامها أكثر في C ببساطة B/C هم عبارة إذا كانت ذات قيمة ، لذلك يقلل من التكرار أو المتغيرات غير الضرورية:

x = (y < 100) ? "dog" :
    (y < 150) ? "cat" :
    (y < 300) ? "bar" : "baz";

عوضا عن

     if (y < 100) { x = "dog"; } 
else if (y < 150) { x = "cat"; }
else if (y < 300) { x = "bar"; } 
else              { x = "baz"; }

في مهام مثل هذه ، أجد أنه من غير الواضح ، وأكثر وضوحًا.

عندما أعمل في روبي من ناحية أخرى ، من المرجح أن أستخدمه if...else...end لأنه تعبير أيضًا.

x =   if (y < 100) then "dog"
    elif (y < 150) then "cat"
    elif (y < 300) then "bar"
    else                "baz"
    end

(على الرغم من أنه من المسلم به ، لشيء بسيط ، قد أستخدم المشغل الثلاثي على أي حال).

الثلاثية ?: المشغل هو مجرد مكافئ وظيفي للإجرائي if بناء. طالما أنك لا تستخدم المتداخلة ?: تعبيرات ، تنطبق الحجج الخاصة بـ/ضد التمثيل الوظيفي لأي عملية. لكن عمليات التعشيش الثلاثية يمكن أن تؤدي إلى رمز مربك بصراحة (تمرين للقارئ: حاول كتابة المحلل الذي سيتعامل مع الشرطية الثلاثي المتداخلة وستقدر تعقيدها).

ولكن هناك الكثير من المواقف التي الاستخدام المحافظ لـ ?: يمكن أن يؤدي المشغل إلى رمز بالفعل أسهل لقراءة من غير ذلك. فمثلا:

int compareTo(Object object) {
    if((isLessThan(object) && reverseOrder) || (isGreaterThan(object) && !reverseOrder)) {
       return 1;
    if((isLessThan(object) && !reverseOrder) || (isGreaterThan(object) && reverseOrder)) {
       return -1;
    else
      return 0;              
}

الآن قارن ذلك مع هذا:

int compareTo(Object object) {
    if(isLessThan(object))
        return reverseOrder ? 1 : -1;         
    else(isGreaterThan(object))
        return reverseOrder ? -1 : 1;
    else        
       return 0;              
}

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

إنها مسألة الأناقة ، حقًا ؛ القواعد اللاواعية التي أميل إلى اتباعها هي:

  • فقط تقييم 1 التعبير - هكذا foo = (bar > baz) ? true : false, ، لكن لا foo = (bar > baz && lotto && someArray.Contains(someValue)) ? true : false
  • إذا كنت أستخدمه لمنطق العرض ، على سبيل المثال <%= (foo) ? "Yes" : "No" %>
  • فقط استخدمه حقًا للواجب ؛ لا تتدفق أبدًا (لذلك أبدا (foo) ? FooIsTrue(foo) : FooIsALie(foo) ) منطق التدفق في الثلاثية هو نفسه كذبة ، تجاهل تلك النقطة الأخيرة.

يعجبني لأنه موجز وأنيق لعمليات التعيين البسيطة.

مثل العديد من أسئلة الرأي ، الإجابة حتما: هذا يعتمد

لشيء مثل:

return x ? "Yes" : "No";

أنا أعتقد أن هذا هو كثير أكثر إيجازا (وأسرع بالنسبة لي لتحليل) من:

if (x) {
    return "Yes";
} else {
    return "No";
}

الآن إذا كان تعبيرك الشرطي معقدًا ، فإن العملية الثلاثية ليست خيارًا جيدًا. شيء مثل:

x && y && z >= 10 && s.Length == 0 || !foo

ليس مرشحًا جيدًا للمشغل الثلاثية.

جانبا ، إذا كنت مبرمجًا C ، فإن GCC لديه بالفعل امتداد يتيح لك ذلك استبعاد جزء إذا كان من Ternary ، مثل هذا:

/* 'y' is a char * */
const char *x = y ? : "Not set";

التي سوف تضع x إلى y على افتراض y ليس NULL. أشياء جيدة.

في رأيي ، من المنطقي فقط استخدام المشغل الثلاثية في الحالات التي يلزم التعبير فيها.

في حالات أخرى ، يبدو أن المشغل الثلاثية يقلل من الوضوح.

بمقياس التعقيد السيكلومي, ، استخدام if البيانات أو المشغل الثلاثية مكافئة. لذلك بهذا التدبير ، الجواب هو رقم, ، سيكون التعقيد هو نفسه كما كان من قبل.

من خلال مقاييس أخرى مثل قابلية القراءة ، والصيانة ، والجافة (لا تكرر بنفسك) ، قد يكون إما الاختيار أفضل من الآخر.

أنا أستخدمه في كثير من الأحيان في الأماكن التي أقيد فيها للعمل في مُنشئ - على سبيل المثال ، بنيات .NET 3.5 LINQ إلى XML الجديدة - لتحديد القيم الافتراضية عندما تكون المعلمة الاختيارية فارغة.

مثال مفتعل:

var e = new XElement("Something",
    param == null ? new XElement("Value", "Default")
                  : new XElement("Value", param.ToString())
);

أو (شكرًا لأستريتيت)

var e = new XElement("Something",
    new XElement("Value",
        param == null ? "Default"
                      : param.ToString()
    )
);

بغض النظر عما إذا كنت تستخدم المشغل الثلاثي أم لا ، فإن التأكد من أن الكود الخاص بك قابل للقراءة هو الشيء المهم. أي بناء يمكن أن يكون غير قابل للقراءة.

أستخدم عامل التشغيل الثلاثي حيثما أمكنني ذلك، إلا إذا كان يجعل قراءة الكود صعبة للغاية، ولكن هذا عادة ما يكون مجرد إشارة إلى أن الكود الخاص بي يمكن أن يحتاج إلى القليل من إعادة البناء.

يحيرني دائمًا كيف يعتقد بعض الناس أن العامل الثلاثي هو ميزة "مخفية" أو غامض إلى حد ما.إنها واحدة من أول الأشياء التي تعلمتها عندما بدأت البرمجة بلغة C، ولا أعتقد أنها تقلل من سهولة القراءة على الإطلاق.إنه جزء طبيعي من اللغة.

وأنا أتفق مع Jmulder: لا ينبغي استخدامه بدلاً من if, ، ولكن لها مكانها للتعبير عن التعبير أو داخل تعبير:

echo "Result: " + n + " meter" + (n != 1 ? "s" : "");
return a == null ? "null" : a;

السابق هو مجرد مثال ، يجب استخدام دعم I18N أفضل من الجمع!

(اختراق اليوم)

#define IF(x) x ?
#define ELSE :

ثم يمكنك أن تفعل ما إذا كان else كتعبير:

int b = IF(condition1)    res1
        ELSE IF(condition2)  res2
        ELSE IF(conditions3) res3
        ELSE res4;

إذا كنت تستخدم المشغل الثلاثية لإجراءات مشروطة بسيطة ، فأعتقد أنه جيد. لقد رأيته (AB) يستخدم للتحكم في تدفق البرنامج دون حتى إجراء مهمة ، وأعتقد أنه ينبغي تجنب ذلك. استخدم بيان if في هذه الحالات.

أعتقد أنه يجب استخدام المشغل الثلاثية عند الحاجة. من الواضح أنه خيار شخصي للغاية ، لكنني أجد أن التعبير البسيط (خاصة كتعبير عائد) أكثر وضوحًا من الاختبار الكامل. مثال في C/C ++:

return (a>0)?a:0;

مقارنة ب:

if(a>0) return a;
else return 0;

لديك أيضًا الحالة التي يكون فيها الحل بين المشغل الثلاثي وإنشاء وظيفة. على سبيل المثال في بيثون:

l = [ i if i > 0 else 0 for i in lst ]

البديل هو:

def cap(value):
    if value > 0:
        return value
    return 0
l = [ cap(i) for i in lst ]

هناك حاجة بما فيه الكفاية أنه في بيثون (كمثال) ، يمكن رؤية مثل هذا المصطلح بانتظام:

l = [ ((i>0 and [i]) or [0])[0] for i in lst ]

يستخدم هذا الخط خصائص المشغلين المنطقيين في بيثون: فهي كسول ويعيد القيمة الأخيرة المحسوبة إذا كانت مساوية للحالة النهائية.

أنا أحب. لا أعرف لماذا ، لكني أشعر برودة شديدة عندما أستخدم التعبير الثلاثية.

لقد رأيت مثل هذه الوحوش مثل (كان الأمر أسوأ في الواقع لأنه كان Isvaliddate ويتحقق من الشهر واليوم أيضًا ، لكنني لم أستطع أن أزعج محاولة تذكر كل شيء):

isLeapYear =
    ((yyyy % 400) == 0)
    ? 1
    : ((yyyy % 100) == 0)
        ? 0
        : ((yyyy % 4) == 0)
            ? 1
            : 0;

حيث ، من الواضح ، كانت سلسلة من الاختبارات إذا كانت أفضل (على الرغم من أن هذا لا يزال أفضل من إصدار الماكرو الذي رأيته ذات مرة).

لا مانع من ذلك لأشياء صغيرة مثل:

reportedAge = (isFemale && (Age >= 21)) ? 21 + (Age - 21) / 3 : Age;

أو حتى أشياء صعبة بعض الشيء مثل:

printf ("Deleted %d file%s\n", n, (n == 1) ? "" : "s");

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

int result = do_something();
if( result != 0 )
{
  debug_printf("Error while doing something, code %x (%s)\n", result,
                result == 7 ? "ERROR_YES" :
                result == 8 ? "ERROR_NO" :
                result == 9 ? "ERROR_FILE_NOT_FOUND" :
                "Unknown");
}

لم أستخدم المشغل الثلاثي تقريبًا لأنه كلما استخدمته ، فإن ذلك يجعلني أفكر دائمًا أكثر مما يجب أن أقوم به لاحقًا عندما أحاول الحفاظ عليه.

أحب تجنب الفعل ، ولكن عندما يجعل الرمز أسهل كثيرًا في التقاطه ، سأذهب للشفرة.

انصح:

String name = firstName;

if (middleName != null) {
    name += " " + middleName;
}

name += " " + lastName;

الآن ، هذا مطول بعض الشيء ، لكنني أجدها أكثر قابلية للقراءة من:

String name = firstName + (middleName == null ? "" : " " + middleName)
    + " " + lastName;

أو:

String name = firstName;
name += (middleName == null ? "" : " " + middleName);
name += " " + lastName;

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

حسنًا ، بناء الجملة له أمر فظيع. أجد IFS وظيفية مفيدة للغاية ، وغالبًا ما يجعل الكود أكثر قابلية للقراءة.

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

فقط عندما:

$ var = (Simple> test؟ simple_result_1: simple_result_2) ؛

قبلة.

عادة ما أستخدم في أشياء مثل هذه:

before:

if(isheader)
    drawtext(x,y,WHITE,string);
else
    drawtext(x,y,BLUE,string);

after:

    drawtext(x,y,isheader==true?WHITE:BLUE,string);

كما أشار الآخرون أنهم لطيفون لظروف بسيطة قصيرة. أنا أحبهم بشكل خاص للافتراضات (نوع من مثل || و أو الاستخدام في JavaScript و Python) ، على سبيل المثال

int repCount = pRepCountIn ? *pRepCountIn : defaultRepCount;

استخدام شائع آخر هو تهيئة مرجع في C ++. نظرًا لأنه يجب إعلان المراجع وتهيئتها في نفس البيان ، لا يمكنك استخدام بيان if.

SomeType& ref = pInput ? *pInput : somethingElse;

أنا أعامل المشغلين الثلاثية مثل غوتو. لديهم مكانهم ، لكنهم شيء يجب أن تجنب عادةً تسهيل فهم الكود.

لقد رأيت مؤخرًا تباينًا على المشغلين الثلاثية (حسنًا ، نوعًا ما) يصنع المعيار "()؟:" يبدو أن البديل هو paragon من الوضوح:

var Result = [CaseIfFalse, CaseIfTrue][(boolean expression)]

أو ، لإعطاء مثال أكثر واقعية:

var Name = ['Jane', 'John'][Gender == 'm'];

ضع في اعتبارك أن هذا JavaScript ، لذلك قد لا تكون أشياء من هذا القبيل ممكنة بلغات أخرى (لحسن الحظ).

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

neseting سيكون لا شيء كبير في كتابي.

لذلك ، استئناف ، من أجل واحد إذا/آخر سأستخدم المشغل الثلاثية. لحالات أخرى ، إذا/آخر إذا/آخر (أو التبديل)

أحب حالة Groovy الخاصة للمشغل الثلاثية ، والتي تسمى مشغل Elvis :؟:

expr ?: default

يتم تقييم هذا الرمز إلى Expr إذا لم يكن فارغًا ، ويفصحًا إذا كان كذلك. من الناحية الفنية ، فإنه ليس مشغلًا ثلاثيًا حقًا ، لكنه بالتأكيد مرتبط به ويوفر الكثير من الوقت/الكتابة.

للحصول على مهام بسيطة مثل تعيين قيمة مختلفة اعتمادًا على حالة فهي رائعة. لن أستخدمها عندما تكون هناك تعبيرات أطول اعتمادًا على الحالة.

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

كمسألة جانبية ، قد أشير أيضًا إلى أن وجودها في الواقع هو في الواقع بعض الحالات بسبب حقيقة أن اختبار المقارنة في C ، هو بيان. في الأيقونة ، if بناء (مثل معظم الأيقونة) هو في الواقع تعبير. لذلك يمكنك أن تفعل أشياء مثل:

x[if y > 5 then 5 else y] := "Y" 

... والتي أجدها أكثر قابلية للقراءة من مشغل مقارنة ternery. :-)

كان هناك مناقشة مؤخرًا حول إمكانية إضافة ?: مشغل إلى أيقونة ، لكن العديد من الأشخاص أشاروا بشكل صحيح إلى أنه لم تكن هناك حاجة على الإطلاق بسبب الطريق if يعمل.

مما يعني أنه إذا كان بإمكانك القيام بذلك في C (أو أي من اللغات الأخرى التي لديها مشغل Ternery) ، فلن تحتاج ، في الواقع ، إلى مشغل Ternery على الإطلاق.

إذا فهمت أنت وزملائك في العمل ما يفعلونه ولم يتم إنشاؤهم في مجموعات ضخمة ، أعتقد أنهم يجعلون الكود أقل تعقيدًا وأسهل في القراءة لأن هناك ببساطة رمز.

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top