Terkadang, sebuah if
pernyataan bisa jadi agak rumit atau panjang, jadi demi keterbacaan, lebih baik mengekstrak panggilan rumit sebelum if
.
misalnya ini:
if (SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall())
{
// do stuff
}
ke dalam ini
bool b1 = SomeComplicatedFunctionCall();
bool b2 = OtherComplicatedFunctionCall();
if (b1 || b2)
{
//do stuff
}
(contoh yang diberikan tidak terlalu buruk, itu hanya untuk ilustrasi ... bayangkan panggilan lain dengan banyak argumen, dll.)
Tetapi dengan ekstraksi ini saya kehilangan evaluasi sirkuit pendek (SCE).
- Apakah saya benar-benar kehilangan SCE setiap saat? Apakah ada skenario di mana kompilator diizinkan untuk "mengoptimalkannya" dan masih menyediakan SCE?
- Adakah cara untuk menjaga agar cuplikan kedua tetap terbaca tanpa kehilangan SCE?
Jawaban:
Salah satu solusi alami akan terlihat seperti ini:
bool b1 = SomeCondition(); bool b2 = b1 || SomeOtherCondition(); bool b3 = b2 || SomeThirdCondition(); // any other condition bool bn = bn_1 || SomeFinalCondition(); if (bn) { // do stuff }
Manfaatnya adalah mudah dipahami, dapat diterapkan pada semua kasus, dan memiliki perilaku korsleting.
Ini adalah solusi awal saya: Pola yang baik dalam pemanggilan metode dan badan loop-for adalah sebagai berikut:
if (!SomeComplicatedFunctionCall()) return; // or continue if (!SomeOtherComplicatedFunctionCall()) return; // or continue // do stuff
Seseorang mendapat manfaat kinerja bagus yang sama dari evaluasi sirkuit pendek, tetapi kodenya terlihat lebih mudah dibaca.
sumber
if
" juga merupakan tanda bahwa fungsi atau metode Anda terlalu besar, dan harus dipecah menjadi yang lebih kecil. Itu tidak selalu menjadi cara terbaik tetapi sering kali begitu!b2
dengan benar dan Anda akan mendapatkannyasomeConditionAndSomeotherConditionIsTrue
, tidak terlalu berarti. Juga, saya harus menyimpan banyak variabel di tumpukan mental saya selama latihan ini (dan sampai saya berhenti bekerja dalam ruang lingkup ini). Saya akan menggunakanSJuan76
solusi nomor 2 atau hanya memasukkan semuanya ke dalam fungsi.Saya cenderung memecah kondisi menjadi beberapa baris, yaitu:
if( SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall() ) {
Bahkan saat berurusan dengan banyak operator (&&), Anda hanya perlu memajukan indensi dengan setiap pasangan tanda kurung. SCE masih bekerja - tidak perlu menggunakan variabel. Menulis kode dengan cara ini membuatnya lebih siap untuk saya selama bertahun-tahun. Contoh yang lebih kompleks:
if( one() ||( two()> 1337 &&( three()== 'foo' || four() ) ) || five()!= 3.1415 ) {
sumber
Jika Anda memiliki rantai kondisi yang panjang dan apa yang menyebabkan beberapa korsleting, maka Anda dapat menggunakan variabel sementara untuk menggabungkan beberapa kondisi. Mengambil contoh Anda adalah mungkin untuk melakukan mis
bool b = SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall(); if (b && some_other_expression) { ... }
Jika Anda memiliki kompiler berkemampuan C ++ 11, Anda dapat menggunakan ekspresi lambda untuk menggabungkan ekspresi menjadi fungsi, mirip dengan di atas:
auto e = []() { return SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall(); }; if (e() && some_other_expression) { ... }
sumber
1) Ya, Anda tidak lagi memiliki SCE. Jika tidak, Anda akan memilikinya
bool b1 = SomeComplicatedFunctionCall(); bool b2 = OtherComplicatedFunctionCall();
bekerja dengan satu cara atau yang lain tergantung apakah ada
if
pernyataan nanti. Terlalu rumit.2) Ini berbasis opini, tetapi untuk ekspresi yang cukup kompleks, Anda dapat melakukan:
if (SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall()) {
Jika terlalu rumit, solusi yang pasti adalah membuat fungsi yang mengevaluasi ekspresi dan memanggilnya.
sumber
Anda juga bisa menggunakan:
bool b = someComplicatedStuff(); b = b || otherComplicatedStuff(); // it has to be: b = b || ...; b |= ...; is bitwise OR and SCE is not working then
dan SCE akan bekerja.
Tapi itu tidak lebih mudah dibaca dari pada contoh:
if ( someComplicatedStuff() || otherComplicatedStuff() )
sumber
b = b || otherComplicatedStuff();
spesifik dan @SargeBorsch melakukan pengeditan untuk menghapus SCE. Terima kasih telah memperhatikan saya tentang perubahan itu @Ant.Saya tidak berpikir pengoptimalan seperti itu diperbolehkan; terutama
OtherComplicatedFunctionCall()
mungkin memiliki beberapa efek samping.Saya lebih suka merefaktornya menjadi satu fungsi atau satu variabel dengan nama deskriptif; yang akan menjaga evaluasi hubung singkat dan keterbacaan:
bool getSomeResult() { return SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall(); } ... if (getSomeResult()) { //do stuff }
Dan saat kami menerapkan
getSomeResult()
berdasarkanSomeComplicatedFunctionCall()
danOtherComplicatedFunctionCall()
, kami dapat menguraikannya secara rekursif jika masih rumit.sumber
Tidak, tidak, tetapi diterapkan secara berbeda:
if (SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall()) { // do stuff }
Di sini, kompilator bahkan tidak akan berjalan
OtherComplicatedFunctionCall()
jikaSomeComplicatedFunctionCall()
mengembalikan nilai true.bool b1 = SomeComplicatedFunctionCall(); bool b2 = OtherComplicatedFunctionCall(); if (b1 || b2) { //do stuff }
Di sini, kedua fungsi akan berjalan karena harus disimpan ke dalam
b1
danb2
. Ffb1 == true
makab2
tidak akan dievaluasi (SCE). TapiOtherComplicatedFunctionCall()
sudah dijalankan.Jika
b2
tidak digunakan di tempat lain, kompilator mungkin cukup pintar untuk menyebariskan pemanggilan fungsi di dalam fungsi jika tidak memiliki efek samping yang dapat diamati.Itu tergantung. Apakah Anda perlu
OtherComplicatedFunctionCall()
menjalankan karena efek samping atau kinerja hit fungsi minimal maka Anda harus menggunakan pendekatan kedua untuk keterbacaan. Jika tidak, tetap gunakan SCE melalui pendekatan pertama.sumber
Kemungkinan lain yang korsleting dan memiliki kondisi di satu tempat:
bool (* conditions [])()= {&a, &b, ...}; // list of conditions bool conditionsHold = true; for(int i= 0; i < sizeOf(conditions); i ++){ if (!conditions[i]()){; conditionsHold = false; break; } } //conditionsHold is true if all conditions were met, otherwise false
Anda dapat menempatkan loop ke dalam fungsi dan membiarkan fungsi menerima daftar kondisi dan mengeluarkan nilai boolean.
sumber
Sangat aneh: Anda berbicara tentang keterbacaan ketika tidak ada yang menyebutkan penggunaan komentar di dalam kode:
if (somecomplicated_function() || // let me explain what this function does someother_function()) // this function does something else ...
Selain itu, saya selalu mengawali fungsi saya dengan beberapa komentar, tentang fungsi itu sendiri, tentang input dan outputnya, dan terkadang saya memberikan contoh, seperti yang Anda lihat di sini:
/*---------------------------*/ /*! interpolates between values * @param[in] X_axis : contains X-values * @param[in] Y_axis : contains Y-values * @param[in] value : X-value, input to the interpolation process * @return[out] : the interpolated value * @example : interpolate([2,0],[3,2],2.4) -> 0.8 */ int interpolate(std::vector<int>& X_axis, std::vector<int>& Y_axis, int value)
Jelas sekali pemformatan yang digunakan untuk komentar Anda mungkin bergantung pada lingkungan pengembangan Anda (Visual studio, JavaDoc di bawah Eclipse, ...)
Sejauh menyangkut SCE, saya berasumsi bahwa yang Anda maksud adalah sebagai berikut:
bool b1; b1 = somecomplicated_function(); // let me explain what this function does bool b2 = false; if (!b1) { // SCE : if first function call is already true, // no need to spend resources executing second function. b2 = someother_function(); // this function does something else } if (b1 || b2) { ... }
sumber
Keterbacaan diperlukan jika Anda bekerja di perusahaan dan kode Anda akan dibaca oleh orang lain. Jika Anda menulis program untuk diri sendiri, terserah Anda jika Anda ingin mengorbankan kinerja demi kode yang dapat dipahami.
sumber