if ifadesi - kısa devre değerlendirmesi ve okunabilirlik


90

Bazen, bir ififade oldukça karmaşık veya uzun olabilir, bu nedenle okunabilirlik açısından karmaşık çağrıları if.

örneğin bu:

if (SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall())
{
    // do stuff
}

bunun içine

bool b1 = SomeComplicatedFunctionCall();
bool b2 = OtherComplicatedFunctionCall();

if (b1 || b2)
{
    //do stuff
}

(verilen örnek o kadar da kötü değil , sadece örnek amaçlı ... birden fazla argüman içeren diğer çağrıları hayal edin, vb.)

Ancak bu ekstraksiyonla kısa devre değerlendirmesini (SCE) kaybettim.

  1. Gerçekten her seferinde SCE'yi kaybeder miyim? Derleyicinin "optimize etmesine" ve yine de SCE sağlamasına izin verilen bazı senaryolar var mı?
  2. İkinci pasajın gelişmiş okunabilirliğini SCE'yi kaybetmeden korumanın yolları var mı?

20
Uygulama, burada veya başka yerlerde göreceğiniz performansla ilgili çoğu yanıtın çoğu durumda yanlış olduğunu gösterir (4 yanlış 1 doğru). Benim tavsiyem her zaman bir profilleme yapın ve bunu kendiniz kontrol edin, "erken optimizasyon" dan kaçınacak ve yeni şeyler öğreneceksiniz.
Marek R

25
@MarekR sadece performansla ilgili değil, OtherCunctionCall'daki olası yan etkilerle ilgili ...
relaxxx

3
Diğer siteleri bahsederken o @ David, bu noktaya genellikle yararlıdır Çapraz nakil hoş karşılanmaz
tatarcık

7
Okunabilirlik birincil endişenizse, eğer koşullu içinde yan etkileri olan işlevleri çağırmayın
Morgen

3
Potansiyel yakın seçmenler: soruyu tekrar okuyun. Kısım (1) fikir temelli değildir , ancak (2) numaralı kısım, benim yapmak üzere olduğum varsayılan herhangi bir "en iyi uygulamaya" atıfta bulunmayı ortadan kaldıran bir düzenleme yoluyla kolayca fikir temelli olmayı bırakabilir.
2016

Yanıtlar:


119

Doğal bir çözüm şuna benzer:

bool b1 = SomeCondition();
bool b2 = b1 || SomeOtherCondition();
bool b3 = b2 || SomeThirdCondition();
// any other condition
bool bn = bn_1 || SomeFinalCondition();

if (bn)
{
  // do stuff
}

Bu, anlaşılması kolay, tüm durumlara uygulanabilir ve kısa devre davranışına sahip olma avantajlarına sahiptir.


Bu benim ilk çözümümdü: Yöntem çağrılarında ve for-döngü gövdelerinde iyi bir model şudur:

if (!SomeComplicatedFunctionCall())
   return; // or continue

if (!SomeOtherComplicatedFunctionCall())
   return; // or continue

// do stuff

Biri, kısa devre değerlendirmesinin aynı güzel performans avantajlarını elde eder, ancak kod daha okunaklı görünür.


4
@relaxxx: Anlıyorum, ama "bundan sonra yapılacak daha fazla şey if" de işlevinizin veya yönteminizin çok büyük olduğunun ve daha küçük parçalara bölünmesi gerektiğinin bir işaretidir. Her zaman en iyi yol değil ama çoğu zaman öyle!
mike3996

2
bu beyaz liste ilkesini ihlal ediyor
JoulinRouge

13
@JoulinRouge: İlginç, bu prensibi hiç duymamıştım. Okunabilirlik üzerindeki faydaları için ben de bu "kısa devre" yaklaşımını tercih ediyorum: girintileri azaltır ve girintili bloktan sonra bir şey olma olasılığını ortadan kaldırır.
Matthieu M.

2
Daha okunaklı mı? b2Doğru isim verirseniz someConditionAndSomeotherConditionIsTrue, süper anlamlı değil alırsınız . Ayrıca, bu egzersiz sırasında (ve bu kapsamda çalışmayı bırakana kadar tbh) zihinsel birikimimde bir dizi değişkeni tutmam gerekiyor. Birlikte gider SJuan76bireyin 2 numaralı çözümü ya da sadece bir işlevde şeyi koydu.
Nathan Cooper

2
Tüm yorumları okumadım ama hızlı bir aramadan sonra ilk kod parçacığının büyük bir avantajı, yani hata ayıklama bulamadım. Öğeleri önceden bir değişkene atamak yerine doğrudan if-ifadesine yerleştirmek ve bunun yerine değişkeni kullanmak, hata ayıklamayı olması gerekenden daha zor hale getirir. Değişkenlerin kullanılması ayrıca değerlerin anlamsal olarak gruplanmasına izin vererek okunabilirliği artırır.
rbaleksandar

30

Koşulları birden çok satıra bölme eğilimindeyim, yani:

if( SomeComplicatedFunctionCall()
 || OtherComplicatedFunctionCall()
  ) {

Birden fazla operatörle (&&) uğraşırken bile, her bir parantez çifti ile girintiyi artırmanız yeterlidir. SCE hala devreye giriyor - değişken kullanmaya gerek yok. Bu şekilde kod yazmak, onu yıllardır benim için çok daha okunaklı hale getirdi. Daha karmaşık örnek:

if( one()
 ||( two()> 1337
  &&( three()== 'foo'
   || four()
    )
   )
 || five()!= 3.1415
  ) {

28

Uzun koşul zincirleriniz varsa ve bazı kısa devreyi neyin koruyacağını düşünüyorsanız, birden çok koşulu birleştirmek için geçici değişkenler kullanabilirsiniz. Örneğinizi alırsanız, örneğin yapmak mümkün olacaktır.

bool b = SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall();
if (b && some_other_expression) { ... }

C ++ 11 özellikli bir derleyiciniz varsa , yukarıdakine benzer şekilde ifadeleri işlevlerle birleştirmek için lambda ifadelerini kullanabilirsiniz :

auto e = []()
{
    return SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall();
};

if (e() && some_other_expression) { ... }

21

1) Evet, artık SCE'niz yok. Aksi takdirde, buna sahip olurdunuz

bool b1 = SomeComplicatedFunctionCall();
bool b2 = OtherComplicatedFunctionCall();

şuna bağlı olarak şu ya da bu şekilde çalışır: ifdaha sonra ifade . Çok karmaşık.

2) Bu görüş temellidir, ancak makul ölçüde karmaşık ifadeler için şunları yapabilirsiniz:

if (SomeComplicatedFunctionCall()
    || OtherComplicatedFunctionCall()) {

Yol çok karmaşıksa, açık çözüm, ifadeyi değerlendiren ve onu çağıran bir işlev oluşturmaktır.


21

Ayrıca kullanabilirsin:

bool b = someComplicatedStuff();
b = b || otherComplicatedStuff(); // it has to be: b = b || ...;  b |= ...; is bitwise OR and SCE is not working then 

ve SCE çalışacaktır.

Ancak, örneğin, daha okunaklı değil:

if (
    someComplicatedStuff()
    ||
    otherComplicatedStuff()
   )

3
Boole'ları bitsel bir operatörle birleştirmeye istekli değilim, bu bana iyi yazılmış gibi görünmüyor. Genel olarak, çok düşük seviyede çalışmadığım ve işlemci döngüleri sayılmadığı sürece en okunabilir görünen şeyi kullanırım.
Ant

3
Özel olarak kullandım b = b || otherComplicatedStuff();ve @SargeBorsch SCE'yi kaldırmak için bir düzenleme yaptı. Beni bu değişikliği fark ettiğiniz için teşekkürler @Ant.
KIIV

14

1) Her seferinde gerçekten SCE kaybediyor muyum? Derleyicinin bazı senaryoların "optimize etmesine" ve yine de SCE sağlamasına izin veriliyor mu?

Böyle bir optimizasyona izin verildiğini sanmıyorum; özellikle OtherComplicatedFunctionCall()bazı yan etkileri olabilir.

2) Böyle bir durumda en iyi uygulama nedir? İhtiyacım olan her şeyin doğrudan içinde olması ve "olabildiğince okunabilir olacak şekilde biçimlendirilmesi" tek olasılık mı (SCE'yi istediğimde)?

Ben onu bir fonksiyon veya tanımlayıcı bir isimle bir değişken halinde yeniden düzenlemeyi tercih ediyorum; hem kısa devre değerlendirmesini hem de okunabilirliği koruyacak:

bool getSomeResult() {
    return SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall();
}

...

if (getSomeResult())
{
    //do stuff
}

Ve biz uygularken getSomeResult()dayanan SomeComplicatedFunctionCall()ve OtherComplicatedFunctionCall()hala karmaşık eğer biz yinelemeli onları ayrıştırmak olabilir.


2
Bunu beğendim çünkü sarmalayıcı işlevine açıklayıcı bir ad vererek biraz okunabilirlik kazanabilirsiniz (muhtemelen getSomeResult olmasa da), diğer birçok yanıt gerçekten değerli bir şey
katmaz

9

1) Her seferinde gerçekten SCE kaybediyor muyum? Derleyicinin bazı senaryoların "optimize etmesine" ve yine de SCE sağlamasına izin veriliyor mu?

Hayır yok, ama farklı şekilde uygulanıyor:

if (SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall())
{
    // do stuff
}

Burada, derleyici bile yayınlanmaz OtherComplicatedFunctionCall()eğer SomeComplicatedFunctionCall()döner gerçek.

bool b1 = SomeComplicatedFunctionCall();
bool b2 = OtherComplicatedFunctionCall();

if (b1 || b2)
{
    //do stuff
}

Burada, her iki işlev de çalışacaktır çünkü b1ve içine depolanmaları gerekir b2. Ff b1 == truedaha sonra b2değerlendirilmez (SCE). FakatOtherComplicatedFunctionCall() zaten çalıştırıldı.

Başka b2hiçbir yerde kullanılmazsa, derleyici işlevin gözlemlenebilir yan etkileri yoksa işlev çağrısının içinde satır içi olacak kadar akıllı olabilir .

2) Böyle bir durumda en iyi uygulama nedir? İhtiyacım olan her şeyin doğrudan içinde olması ve "mümkün olduğunca okunabilir olacak şekilde biçimlendirilmesi" tek olasılık mı (SCE'yi istediğimde)?

Bu bağlıdır. Yan etkiler nedeniyle koşmanız gerekiyor mu OtherComplicatedFunctionCall()veya işlevin performans düşüşü minimum düzeyde ise, okunabilirlik için ikinci yaklaşımı kullanmalısınız. Aksi takdirde, ilk yaklaşımda SCE'ye bağlı kalın.


8

Kısa devrelerin ve koşulların tek bir yerde bulunmasının başka bir olasılığı:

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

Döngüyü bir işleve koyabilir ve işlevin bir koşullar listesini kabul etmesine ve bir boole değeri vermesine izin verebilirsiniz.


1
@Erbureth Hayır değiller. Dizinin öğeleri işlev işaretçileridir, işlevler döngüde çağrılana kadar çalıştırılmazlar.
Barmar

Teşekkürler Barmar, ancak bir düzenleme yaptım, Erbureth düzenlemeden önce haklıydı (düzenlememin görsel olarak daha doğrudan yayılacağını düşündüm).
levilime

4

Çok garip: Kod içinde kimse yorum kullanımından bahsetmediğinde okunabilirlikten bahsediyorsunuz:

if (somecomplicated_function() || // let me explain what this function does
    someother_function())         // this function does something else
...

Bunun da ötesinde, işlevlerimi her zaman bazı yorumlarla, işlevin kendisi, girdisi ve çıktısı hakkında bazı yorumlarla ilerletiyorum ve bazen burada görebileceğiniz gibi bir örnek veriyorum:

/*---------------------------*/
/*! 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)

Açıkçası, yorumlarınız için kullanılacak biçimlendirme, geliştirme ortamınıza bağlı olabilir (Visual studio, Eclipse altında JavaDoc, ...)

SCE söz konusu olduğunda, bununla aşağıdakileri kastettiğinizi varsayıyorum:

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) {
...
}

-7

Bir şirkette çalışıyorsanız ve kodunuz başka biri tarafından okunacaksa okunabilirlik gereklidir. Kendiniz için bir program yazarsanız, anlaşılır bir kod uğruna performanstan ödün vermek isteyip istemediğiniz size kalmıştır.


24
"Altı ay sonra sen" kesinlikle "başka biri" olduğunu ve "yarın sen" bazen olabileceğin akılda tutulursa. Bir performans sorunu olduğuna dair sağlam kanıtlar elde edene kadar, okunabilirlikten asla ödün vermem.
Martin Bonner Monica'yı
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.