Manfaat menggunakan operator bersyarat?: (Terner)

101

Apa keuntungan dan kerugian dari operator?: Dibandingkan dengan pernyataan standar if-else. Yang jelas adalah:

Bersyarat?: Operator

  • Lebih pendek dan lebih ringkas saat menangani perbandingan dan penugasan nilai langsung
  • Tampaknya tidak sefleksibel konstruksi if / else

Standar If / Else

  • Dapat diterapkan ke lebih banyak situasi (seperti panggilan fungsi)
  • Seringkali terlalu panjang

Keterbacaan tampaknya bervariasi untuk masing-masing tergantung pada pernyataannya. Untuk beberapa saat setelah pertama kali terpapar ke operator?:, Saya butuh beberapa waktu untuk mencerna persis bagaimana cara kerjanya. Apakah Anda akan merekomendasikan untuk menggunakannya sedapat mungkin, atau tetap menggunakan if / else mengingat saya bekerja dengan banyak non-programmer?

KChaloux
sumber
8
Anda sudah mendapatkan intinya.
Byron Whitlock
1
@Nicholas Knight: Saya menduga OP berarti Anda tidak dapat melakukannya, misalnya, SomeCheck() ? DoFirstThing() : DoSecondThing();- Anda harus menggunakan ekspresi untuk mengembalikan nilai.
Dan Tao
6
Gunakan di tempat yang jelas , tetap gunakan jika / lain jika tidak. Kejelasan kode harus menjadi pertimbangan utama Anda.
hollsk
8
Sudahkah kau melihat '??' namun? Serius, jika menurutmu terner itu keren ...
pdr
3
1 untuk tidak menyebutnya hanya "operator terner" seperti yang dilakukan banyak orang. Meskipun itu adalah satu-satunya operator terner (sebagai lawan unary dan binary) di C #, itu bukan namanya.
John M Gant

Jawaban:

122

Saya pada dasarnya akan merekomendasikan menggunakannya hanya ketika pernyataan yang dihasilkan sangat pendek dan mewakili peningkatan yang signifikan dalam keringkasan di atas setara if / else tanpa mengorbankan keterbacaan.

Contoh yang baik:

int result = Check() ? 1 : 0;

Contoh buruk:

int result = FirstCheck() ? 1 : SecondCheck() ? 1 : ThirdCheck() ? 1 : 0;
Dan Tao
sumber
5
Keputusan yang bagus, tapi sebagai catatan, itu adalah "kesimpulan".
mqp
6
@mquander, Anda yakin tentang itu? merriam-webster.com/dictionary/concise
Byron Whitlock
39
Saya selalu memulai dengan yang sederhana dan membuatnya lebih kompleks dari waktu ke waktu hingga benar-benar tidak dapat dibaca.
Jouke van der Maas
9
Keterbacaan pada contoh kedua dapat dengan mudah diperbaiki dengan format yang lebih baik. Tapi, seperti yang disarankan OP, itu tergantung pada keterbacaan dan kesederhanaan versus verbositas.
Nathan Ernst
4
Bukan bagian dari pertanyaan OP, tetapi penting untuk diperhatikan adalah kenyataan bahwa Anda tidak dapat returnmenjadi bagian dari hasil operasi terner. Misalnya: check() ? return 1 : return 0;tidak akan berhasil, tetapi return check() ? 1 : 0;akan. Selalu menyenangkan menemukan keanehan kecil ini dalam pemrograman.
CSS
50

Ini cukup banyak tercakup oleh jawaban lain, tetapi "itu adalah ekspresi" tidak benar-benar menjelaskan mengapa itu sangat berguna ...

Dalam bahasa seperti C ++ dan C #, Anda dapat menentukan bidang hanya baca lokal (dalam badan metode) menggunakannya. Ini tidak mungkin dilakukan dengan pernyataan if / then konvensional karena nilai bidang hanya baca harus ditetapkan dalam pernyataan tunggal tersebut:

readonly int speed = (shiftKeyDown) ? 10 : 1;

tidak sama dengan:

readonly int speed;  
if (shifKeyDown)  
    speed = 10;    // error - can't assign to a readonly
else  
    speed = 1;     // error  

Dengan cara yang sama Anda bisa menyematkan ekspresi tersier di kode lain. Selain membuat kode sumber lebih ringkas (dan dalam beberapa kasus lebih mudah dibaca), ini juga dapat membuat kode mesin yang dihasilkan lebih ringkas dan efisien:

MoveCar((shiftKeyDown) ? 10 : 1);

... mungkin menghasilkan lebih sedikit kode daripada harus memanggil metode yang sama dua kali:

if (shiftKeyDown)
    MoveCar(10);
else
    MoveCar(1);

Tentu saja, ini juga bentuk yang lebih nyaman dan ringkas (lebih sedikit mengetik, lebih sedikit pengulangan, dan dapat mengurangi kemungkinan kesalahan jika Anda harus menduplikasi potongan kode di if / else). Dalam kasus "pola umum" bersih seperti ini:

object thing = (reference == null) ? null : reference.Thing;

... itu hanya lebih cepat untuk membaca / parse / memahami (setelah Anda terbiasa) daripada bertele-tele jika / lain setara, sehingga dapat membantu Anda untuk 'grok' kode lebih cepat.

Tentu saja, hanya karena berguna tidak berarti itu adalah hal terbaik untuk digunakan dalam setiap kasus. Saya menyarankan hanya menggunakannya untuk potongan kode pendek di mana artinya jelas (atau dibuat lebih jelas) dengan menggunakan ?:- jika Anda menggunakannya dalam kode yang lebih kompleks, atau menyarangkan operator terner satu sama lain, itu dapat membuat kode sangat sulit untuk dibaca .

Jason Williams
sumber
@JaminGrey "itu tidak berarti bahwa, ketika konstanta dibuat, ia disetel ke 10 atau 1." Apakah maksud Anda maksudnya itu? Komentar yang salah dapat menyebabkan lebih banyak kebingungan bagi pemrogram C ++ baru daripada masalah yang Anda coba
selesaikan
5
Untuk pembaca selanjutnya yang menemukan ini, dengan " const int speed = (shiftKeyDown)? 10: 1; ", itu berarti bahwa ketika konstanta pertama kali dibuat , itu disetel ke 10 atau 1. Itu tidak berarti bahwa setiap kali konstanta diakses itu melakukan pemeriksaan. (Hanya memetikan programmer C ++ yang lebih baru bingung)
Jamin Grey
2
... atau dengan kata lain, a constadalah konstan, yaitu tidak dapat diubah setelah pernyataan yang dideklarasikan telah dieksekusi.
Jason Williams
1
@Jamur_kejang. Bukankah seharusnya begitu readonly? Saya selalu berpikir constberarti " diselesaikan pada waktu kompilasi dan sejalan di mana saja digunakan ".
Nolonar
1
@ColinWiseman, ini merupakan contoh untuk menggambarkan bagaimana:? Dapat digunakan. Saya secara khusus menyatakan bahwa hanya karena Anda dapat melakukannya, itu tidak berarti bahwa itu adalah hal yang "terbaik" untuk dilakukan dalam kasus tertentu. Untuk mengetahuinya, pembaca diharapkan menggunakan otak mereka setiap kali mereka menemukan kasus yang mungkin berguna bagi mereka.
Jason Williams
14

Saya biasanya memilih operator terner ketika saya memiliki banyak kode duplikat sebaliknya.

if (a > 0)
    answer = compute(a, b, c, d, e);
else
    answer = compute(-a, b, c, d, e);

Dengan operator terner, ini dapat dilakukan dengan yang berikut ini.

answer = compute(a > 0 ? a : -a, b, c, d, e); 
Ryan Bright
sumber
12
secara pribadi saya akan melakukan aVal = a > 0 ? a : -a; answer = compute(aVal,b,c,d,e);Terutama jika b, c, ddan ediperlukan pengobatan juga.
corsiKa
10
Mengapa menggunakan kondisional sama sekali dalam contoh ini? Cukup dapatkan Abs (a) dan panggil compute () sekali.
Ash
2
Ya, saya tidak membuat contoh terbaik. :)
Ryan Bright
Bagi seorang pemula, itu tidak terlihat sama. Bukankah perlu answer = compute (a> 0? A, b, c, d, e: -a, b, c, d, e); ?
pbreitenbach
@pbreitenbach: tidak - ini masalah prioritas - argumen pertama compute(...)adalah a > 0 ? a : -1, yang semuanya dievaluasi secara terpisah dari argumen lain yang dipisahkan koma. Bagaimanapun, sayangnya C ++ tidak memiliki notasi yang diajukan pertanyaan Anda untuk menangani "tuple" dari nilai yang dipisahkan koma, jadi bahkan a > 0 ? (a, b, c, d, e) : (-a, b, c, d, e)ilegal, dan tidak ada yang sangat mirip yang berfungsi tanpa perubahan pada computedirinya sendiri.
Tony Delroy
12

Saya merasa sangat membantu ketika melakukan pengembangan web jika saya ingin menetapkan variabel ke nilai yang dikirim dalam permintaan jika itu ditentukan atau ke beberapa nilai default jika tidak.

wshato
sumber
3
Nilai default +1 di web dev adalah contoh yang sangat baik tempat yang baik untuk menggunakan operator terner
Byron Whitlock
11

Penggunaan yang sangat keren adalah:

x = foo ? 1 :
    bar ? 2 :
    baz ? 3 :
          4;
aib
sumber
10
Hati-hati dengan ini di PHP, operator terner mengasosiasikan cara yang salah di PHP. Pada dasarnya, jika foosalah, maka semuanya akan dievaluasi menjadi 4 tanpa melakukan tes lainnya.
Tom Busby
4
@TomBusby - Wow. Namun alasan lain untuk membenci PHP, jika Anda adalah seseorang yang sudah membenci PHP.
Todd Lehman
6

Operator bersyarat sangat bagus untuk kondisi pendek, seperti ini:

varA = boolB ? valC : valD;

Saya menggunakannya sesekali karena waktu yang dibutuhkan untuk menulis sesuatu yang lebih sedikit ... sayangnya, percabangan ini terkadang dapat terlewatkan oleh pengembang lain yang menjelajahi kode Anda. Plus, kode biasanya tidak sesingkat itu, jadi saya biasanya membantu keterbacaan dengan meletakkan? dan: pada baris terpisah, seperti ini:

doSomeStuffToSomething(shouldSomethingBeDone()
    ? getTheThingThatNeedsStuffDone()
    : getTheOtherThingThatNeedsStuffDone());

Namun, keuntungan besar menggunakan blok if / else (dan mengapa saya lebih suka mereka) adalah lebih mudah untuk masuk nanti dan menambahkan beberapa logika tambahan ke cabang,

if (shouldSomethingBeDone()) {
    doSomeStuffToSomething(getTheThingThatNeedsStuffDone());
    doSomeAdditionalStuff();
} else {
doSomeStuffToSomething(getTheOtherThingThatNeedsStuffDone());
}

atau tambahkan ketentuan lain:

if (shouldSomethingBeDone()) {
    doSomeStuffToSomething(getTheThingThatNeedsStuffDone());
    doSomeAdditionalStuff();
} else if (shouldThisOtherThingBeDone()){
    doSomeStuffToSomething(getTheOtherThingThatNeedsStuffDone());
}

Jadi, pada akhirnya, ini tentang kenyamanan untuk Anda sekarang (lebih singkat untuk digunakan:?) Vs. kenyamanan untuk Anda (dan orang lain) nanti. Ini adalah keputusan pengadilan ... tetapi seperti semua masalah pemformatan kode lainnya, satu-satunya aturan yang sebenarnya adalah konsisten, dan secara visual sopan kepada mereka yang harus mempertahankan (atau menilai!) Kode Anda.

(semua kode disusun oleh mata)

iandisme
sumber
5

Satu hal yang perlu dikenali saat menggunakan operator terner bahwa itu adalah ekspresi, bukan pernyataan.

Dalam bahasa fungsional seperti skema, perbedaannya tidak ada:

(if (> ab) ab)

Kondisional?: Operator "Sepertinya tidak sefleksibel yang dibangun if / else"

Dalam bahasa fungsional itu.

Ketika memprogram dalam bahasa imperatif, saya menerapkan operator terner dalam situasi di mana saya biasanya akan menggunakan ekspresi (tugas, pernyataan bersyarat, dll).

Ken Struys
sumber
5

Meskipun jawaban di atas valid, dan saya setuju dengan pentingnya keterbacaan, ada 2 hal yang perlu dipertimbangkan:

  1. Dalam C # 6, Anda dapat memiliki metode bertubuh ekspresi.

Ini membuatnya sangat ringkas untuk menggunakan terner:

string GetDrink(DayOfWeek day) 
   => day == DayOfWeek.Friday
      ? "Beer" : "Tea";
  1. Perilaku berbeda dalam hal konversi tipe implisit.

Jika Anda memiliki tipe T1dan T2keduanya dapat secara implisit dikonversi T, maka di bawah ini tidak berfungsi:

T GetT() => true ? new T1() : new T2();

(karena kompilator mencoba menentukan jenis ekspresi terner, dan tidak ada konversi antara T1dan T2.)

Di sisi lain, if/elseversi di bawah ini berfungsi:

T GetT()
{
   if (true) return new T1();
   return new T2();
}

karena T1diubah menjadi Tdan begitu jugaT2

la-yumba
sumber
5

Terkadang hal itu dapat membuat penetapan nilai bool lebih mudah dibaca pada pandangan pertama:

// With
button.IsEnabled = someControl.HasError ? false : true;

// Without
button.IsEnabled = !someControl.HasError;
Tyler Pantuso
sumber
4

Jika saya menetapkan nilai dan saya tahu itu akan selalu menjadi satu baris kode untuk melakukannya, saya biasanya menggunakan operator terner (bersyarat). Jika ada kemungkinan kode dan logika saya akan berubah di masa mendatang, saya menggunakan if / else karena lebih jelas bagi programmer lain.

Yang menarik bagi Anda lebih lanjut mungkin ?? operator .

drharris
sumber
4

Keuntungan dari operator bersyarat adalah bahwa ini adalah operator. Dengan kata lain, ini mengembalikan nilai. Karena ifmerupakan pernyataan, ia tidak dapat mengembalikan nilai.

Gabe
sumber
4

Saya akan merekomendasikan untuk membatasi penggunaan operator terner (? :) menjadi satu baris tugas sederhana jika / lain logika. Sesuatu yang menyerupai pola ini:

if(<boolCondition>) {
    <variable> = <value>;
}
else {
    <variable> = <anotherValue>;
}

Dapat dengan mudah diubah menjadi:

<variable> = <boolCondition> ? <value> : <anotherValue>;

Saya akan menghindari penggunaan operator terner dalam situasi yang memerlukan if / else if / else, bersarang if / else, atau logika cabang if / else yang menghasilkan evaluasi beberapa baris. Menerapkan operator terner dalam situasi ini kemungkinan besar akan menghasilkan kode yang tidak dapat dibaca, membingungkan, dan tidak dapat diatur. Semoga ini membantu.

HOCA
sumber
2

Ada beberapa keuntungan kinerja menggunakan? operator misalnya. MS Visual C ++, tetapi ini benar-benar hal yang spesifik untuk kompiler. Kompilator sebenarnya dapat mengoptimalkan cabang bersyarat dalam beberapa kasus.

darklon
sumber
2

Skenario yang paling saya temukan sendiri menggunakannya adalah untuk nilai default dan terutama dalam pengembalian

return someIndex < maxIndex ? someIndex : maxIndex;

Itu benar-benar satu-satunya tempat yang menurutku bagus, tapi untuk mereka aku lakukan.

Meskipun jika Anda mencari boolean, ini terkadang terlihat seperti hal yang tepat untuk dilakukan:

bool hey = whatever < whatever_else ? true : false;

Karena sangat mudah dibaca dan dipahami, tetapi gagasan itu harus selalu dilemparkan agar lebih jelas:

bool hey = (whatever < whatever_else);
Jimmy Hoffa
sumber
2

Jika Anda membutuhkan banyak cabang dengan kondisi yang sama, gunakan if:

if (A == 6)
  f(1, 2, 3);
else
  f(4, 5, 6);

Jika Anda membutuhkan banyak cabang dengan kondisi berbeda, maka jika jumlah pernyataan akan semakin bertambah, Anda akan ingin menggunakan terner:

f( (A == 6)? 1: 4, (B == 6)? 2: 5, (C == 6)? 3: 6 );

Selain itu, Anda dapat menggunakan operator terner dalam inisialisasi.

const int i = (A == 6)? 1 : 4;

Melakukannya dengan if sangat berantakan:

int i_temp;
if (A == 6)
   i_temp = 1;
else
   i_temp = 4;
const int i = i_temp;

Anda tidak dapat menempatkan inisialisasi di dalam if / else, karena ini mengubah cakupan. Tetapi referensi dan variabel konst hanya dapat diikat saat inisialisasi.

Ben Voigt
sumber
2

Operator terner dapat dimasukkan dalam nilai r, sedangkan jika-maka-lain tidak bisa; di sisi lain, if-then-else dapat mengeksekusi loop dan pernyataan lain, sedangkan operator terner hanya dapat mengeksekusi (kemungkinan void) rvalues.

Di catatan terkait, && dan || operator mengizinkan beberapa pola eksekusi yang lebih sulit diimplementasikan dengan if-then-else. Misalnya, jika seseorang memiliki beberapa fungsi untuk dipanggil dan ingin mengeksekusi sepotong kode jika ada yang gagal, itu dapat dilakukan dengan baik menggunakan operator &&. Melakukannya tanpa operator itu akan membutuhkan kode yang berlebihan, goto, atau variabel bendera tambahan.

supercat
sumber
1

Dengan C # 7 , Anda dapat menggunakan fitur ref lokal baru untuk menyederhanakan penetapan kondisional variabel yang kompatibel dengan ref. Jadi sekarang, Anda tidak hanya dapat melakukan:

int i = 0;

T b = default(T), c = default(T);

// initialization of C#7 'ref-local' variable using a conditional r-value⁽¹⁾

ref T a = ref (i == 0 ? ref b : ref c);

... tetapi juga yang sangat luar biasa:

// assignment of l-value⁽²⁾ conditioned by C#7 'ref-locals'

(i == 0 ? ref b : ref c) = a;

Baris kode tersebut memberikan nilai ake salah satu batau c, bergantung pada nilai i.



Catatan
1. nilai-r adalah sisi kanan dari suatu tugas, nilai yang diberikan.
2. Nilai-l adalah sisi kiri tugas, variabel yang menerima nilai yang ditetapkan.

Glenn Slayden
sumber