Yuvalamayı azaltmak için “if” ifadesini ters çevirin


272

Kodumda ReSharper'ı çalıştırdığımda , örneğin:

    if (some condition)
    {
        Some code...            
    }

ReSharper bana yukarıdaki uyarıyı verdi (yuvalamayı azaltmak için "if" ifadesini ters çevir) ve aşağıdaki düzeltmeyi önerdi:

   if (!some condition) return;
   Some code...

Bunun neden daha iyi olduğunu anlamak istiyorum. Her zaman bir yöntemin ortasında "dönüş" kullanmanın biraz "goto" gibi sorunlu olduğunu düşündüm.


1
Kontrol ve başlangıçta dönen istisna iyi, ama bir şey (yani (bazı koşul) iade) yerine doğrudan istisna için kontrol böylece durumu değiştirmek istiyorum inanıyorum.
bruceatk

36
Hayır, performans için hiçbir şey yapmaz.
Seth Carnegie

3
Benim yöntem yerine kötü veri geçiriliyordu bir ArgumentException atmak cazip olurdu.
asawyer

1
@asawyer Evet, burada bir iddia hatası kullanmanın aksine, saçma girdileri çok affedici işlevler hakkında kapsamlı bir tartışma var. Katı Kod yazmak gözlerimi buna açtı. Bu durumda, böyle bir şey olurdu ASSERT( exampleParam > 0 ).
Greg Hendershott

4
İddialar parametreler için değil, dahili durum içindir. Parametreleri doğrulayarak, dahili durumunuzun doğru olduğunu belirterek ve ardından işlemi gerçekleştirerek işe başlarsınız. Bir sürüm derlemesinde, iddiaları hariç tutabilir veya bir bileşen kapanmasıyla eşleyebilirsiniz.
Simon Richter

Yanıtlar:


296

Yöntemin ortasındaki bir dönüş mutlaka kötü değildir. Kodun amacını daha net hale getirirse, hemen geri dönmek daha iyi olabilir. Örneğin:

double getPayAmount() {
    double result;
    if (_isDead) result = deadAmount();
    else {
        if (_isSeparated) result = separatedAmount();
        else {
            if (_isRetired) result = retiredAmount();
            else result = normalPayAmount();
        };
    }
     return result;
};

Bu durumda, _isDeaddoğruysa, hemen yöntemden çıkabiliriz. Bunun yerine bu şekilde yapılandırmak daha iyi olabilir:

double getPayAmount() {
    if (_isDead)      return deadAmount();
    if (_isSeparated) return separatedAmount();
    if (_isRetired)   return retiredAmount();

    return normalPayAmount();
};   

Bu kodu yeniden düzenleme kataloğundan aldım . Bu özel yeniden düzenleme denir: İç İçe Koşullu Muhafız Cümleleriyle Değiştir.


13
Bu gerçekten güzel bir örnek! Yeniden düzenlenmiş kod daha çok bir vaka ifadesi gibi okunur.
Diğerleri

16
Muhtemelen sadece bir tat meselesi: Okunabilirliği daha da arttırmak için 2. ve 3. "if" if "else" olarak değiştirmenizi öneririm. Birisi "return" ifadesine bakmazsa, aşağıdaki durumun yalnızca bir öncekinin başarısız olması durumunda kontrol edildiği, yani kontrollerin sırasının önemli olduğu açıktır.
foraidt

2
Bir örnekte, bu basit, id, 2. yolun daha iyi olduğunu kabul ediyorum, ancak sadece SO bariz ne olduğu için.
Andrew Bullock

Şimdi, bu güzel kod jopunu nasıl yazdırabiliriz ve görsel stüdyonun onu parçalamasını ve getirileri ayrı satırlara koymasını nasıl önleyebiliriz? Gerçekten bu şekilde kodu yeniden biçimlendirmesi beni rahatsız ediyor. Bu çok okunabilir kodu Çirkin yapar.
Mark T

@Mark T Visual studio'da bu kodu parçalamasını önleyen ayarlar var.
Aaron Smith

333

Sadece estetik değil , aynı zamanda yöntemin içindeki maksimum yuvalama seviyesini de azaltır . Bu genellikle bir artı olarak kabul edilir, çünkü yöntemleri daha kolay anlaşılır hale getirir (ve aslında birçok statik analiz aracı , bunun kod kalitesinin göstergelerinden biri olarak bir ölçü sağlar).

Öte yandan, yönteminizin birden fazla çıkış noktasına sahip olmasını sağlar, başka bir grup insanın hayır olduğuna inandığı bir şey.

Şahsen, ReSharper ve ilk gruba katılıyorum (istisnaları olan bir dilde "çoklu çıkış noktaları" nı tartışmayı aptalca buluyorum; neredeyse her şey fırlatabilir, bu yüzden tüm yöntemlerde çok sayıda potansiyel çıkış noktası vardır).

Performansla ilgili olarak : her iki sürüm de her dilde eşdeğer olmalıdır (IL düzeyinde değilse, o zaman kesinlikle jitter koddan geçtikten sonra). Teorik olarak bu derleyiciye bağlıdır, ancak pratikte günümüzde yaygın olarak kullanılan herhangi bir derleyici, bundan daha gelişmiş kod optimizasyonu durumlarını işleyebilir.


41
tek çıkış noktaları? Kimin ihtiyacı var?
sq33G

3
@ sq33G: SESE (ve elbette cevapları) hakkındaki bu soru harika. Bağlantı için teşekkürler!
Jon

Ben tek çıkış noktaları için savunucu bazı insanlar olduğunu cevapları duyuyorum, ama hiç kimse, özellikle C # gibi dillerde gerçekten savunan birini görmedim.
Thomas Bonini

1
@AndreasBonini: İspat olmaması, yokluğun kanıtı değildir. :-)
Jon

Evet, herkesin bazı insanların başka bir yaklaşımı sevdiğini söylemesi gerektiğini garip buluyorum, eğer o insanlar bunu kendileri söylemeye ihtiyaç duymuyorlarsa =)
Thomas Bonini

102

Bu biraz dini bir argüman, ama ReSharper ile daha az yuva yapmayı tercih etmene katılıyorum. Bunun bir fonksiyondan çoklu dönüş yollarına sahip olmanın olumsuzluklarından daha ağır bastığına inanıyorum.

Daha az yuvaya sahip olmanın temel nedeni, kod okunabilirliğini ve sürdürülebilirliğini artırmaktır . Diğer birçok geliştiricinin gelecekte kodunuzu okuması gerektiğini ve daha az girintili kodun okunması genellikle daha kolay olduğunu unutmayın.

Önkoşullar , işlevin başlangıcında erken dönmenin uygun olduğu durumlara harika bir örnektir. Fonksiyonun geri kalanının okunabilirliği neden bir önkoşul kontrolünün varlığından etkilenmelidir?

Bir yöntemden birden çok kez geri dönmeyle ilgili olumsuzluklara gelince - hata ayıklayıcılar şimdi oldukça güçlüdür ve belirli bir işlevin tam olarak nerede ve ne zaman döndüğünü bulmak çok kolaydır.

Bir işlevde birden fazla geri dönüş olması bakım programcısının işini etkilemez.

Kötü kod okunabilirliği olacaktır.


2
Bir fonksiyondaki çoklu geri dönüşler bakım programlayıcısını etkiler. Bir fonksiyonda hata ayıklarken, haydut dönüş değerini ararken, bakımcı dönebilen tüm yerlere kesme noktaları koymalıdır.
EvilTeach

10
Açılış ayracı için bir kırılma noktası koyardım. Eğer fonksiyona adım atarsanız, sadece kontrollerin sırayla yapıldığını görmekle kalmaz, aynı zamanda o çalışma için hangi fonksiyonun kontrol edildiğini de açıkça göreceksiniz.
John Dunagan

3
John ile burada hemfikirim ... Problemin sadece fonksiyonun tamamında adım atmasını görmüyorum.
Nailer

5
@Nailer Çok geç, özellikle katılıyorum, çünkü işlev adım adım ilerleyemeyecek kadar büyükse, yine de birden fazla işleve ayrılmalıdır!
Aidiakapi

1
John ile de aynı fikirde. Belki de kırılma noktasını aşağıya koymayı düşündüğü eski bir okuldur, ancak bir işlevin ne döndürdüğünü merak ediyorsanız, neden geri döndüğünü döndürdüğünü görmek için bu işlevden geçeceksiniz. Sadece ne döndürdüğünü görmek istiyorsanız, son köşebendine koyun.
user441521

70

Diğerlerinin de belirttiği gibi, bir performans isabeti olmamalı, ancak başka hususlar da var. Bu geçerli endişelerin yanı sıra, bu sizi bazı durumlarda gotcha'lara da açabilir. Bir bununla uğraştığınızı varsayalım double:

public void myfunction(double exampleParam){
    if(exampleParam > 0){
        //Body will *not* be executed if Double.IsNan(exampleParam)
    }
}

Görünüşte eşdeğer ters çevirme ile kontrast :

public void myfunction(double exampleParam){
    if(exampleParam <= 0)
        return;
    //Body *will* be executed if Double.IsNan(exampleParam)
}

Bu nedenle, bazı durumlarda doğru bir şekilde ters çevrilmiş gibi görünen ifşey olmayabilir.


4
Yeniden şekillendirici hızlı düzeltmesinin , beni yakalayan exampleParam <0 yerine exampleParam < 0 yerine exampleParam> 0'ı tersine çevirdiği de not edilmelidir .
nickd

1
İşte bu yüzden geliştiricinin bir nanenin kefaletle sonuçlanması gerektiğini düşündüğü göz önüne alındığında, bu kontrol ön tarafa getirilmeli ve iade edilmelidir.
user441521

51

Sadece bir fonksiyonun sonunda geri dönme fikri, dillerin istisnaları desteklemesinden önceki günlerden döndü. Programların, bir yöntemin sonuna temizleme kodu koyabileceğine ve ardından çağrıldığından ve diğer bazı programcıların, temizleme kodunun atlanmasına neden olan yöntemde bir dönüşü gizlemeyeceğinden emin olmasını sağladı. . Atlanan temizleme kodu bellek veya kaynak sızıntısına neden olabilir.

Ancak, istisnaları destekleyen bir dilde, böyle bir garanti vermez. İstisnaları destekleyen bir dilde, herhangi bir ifadenin veya ifadenin yürütülmesi, yöntemin sona ermesine neden olan bir kontrol akışına neden olabilir. Bu, temizleme işleminin nihayet veya anahtar kelimeler kullanılarak yapılması gerektiği anlamına gelir.

Her neyse, birçok insanın neden iyi bir şey olduğunu anlamadan 'bir yöntemin sonunda geri dönüş' kılavuzundan alıntı yaptığını ve okunabilirliği artırmak için yuvalamanın azaltılmasının muhtemelen daha iyi bir amaç olduğunu düşünüyorum.


6
Sadece istisnaların neden ÇirkinAndEvil [tm] ... ;-) olduğunu anladınız. İstisnalar, fantezi ve pahalı kılıklarda gotos.
EricSchaefer

16
@Eric Son derece kötü bir koda maruz kalmış olmalısınız. Yanlış kullanıldıklarında oldukça belirgindir ve genellikle daha yüksek kalitede kod yazmanıza izin verir.
Robert Paulson

Gerçekten aradığım cevap bu! Burada büyük cevaplar var, ancak çoğu bu tavsiyenin ortaya çıkmasının gerçek nedenine değil, örneklere odaklanıyor.
Gustavo Mori

Bu en iyi cevaptır, çünkü bu argümanın neden ilk başta ortaya çıktığını açıklar.
Buttle Butkus

If you've got deep nesting, maybe your function is trying to do too many things.=> Bu, önceki ifadenizden doğru bir sonuç değil. Çünkü, A kodunu içeren A davranışını D koduyla A davranışına yeniden aktarabileceğinizi söylemeden hemen önce D kodu daha temiz, verilmiş, ancak "çok fazla şey" değişmeyen davranışa atıfta bulunuyor. Bu nedenle bu sonuçla bir anlam ifade etmiyorsunuz.
v.oddou

30

Ben ters çevrilmiş olanlar için bir isim olduğunu eklemek istiyorum - Guard Clause. Mümkün olduğunca kullanıyorum.

Başlangıçta, iki kod ekranı ve başka hiçbir yerde kod okuma nefret ediyorum. Sadece ters çevir ve geri dön. Bu şekilde kimse kaydırma zamanını boşa harcamaz.

http://c2.com/cgi/wiki?GuardClause


2
Kesinlikle. Bu daha çok yeniden düzenleme. Bahsettiğiniz gibi kodun daha kolay okunmasına yardımcı olur.
rpattabi

'dönüş', bir koruma maddesi için yeterli ve korkunç değildir. Yapardım: (ex <= 0) WrongParamValueEx ("[YöntemAdı] Hatalı giriş parametresi 1 değeri {0} ...
atarsa

3
@John. Bu aslında bir hata ise haklısın. Ama çoğu zaman değil. Ve yöntem denilen her yerde bir şey kontrol etmek yerine yöntem sadece denetler ve hiçbir şey yapmadan döner.
Piotr Perak

3
Kodun iki ekranı olan bir yöntemi okumaktan nefret ederdim, nokta, ifs ya da hayır. lol
jpmc26

1
Ben şahsen tam olarak bu nedenle erken dönüşleri tercih ederim (ayrıca: yuvalama kaçınarak). Ancak, bu performansla ilgili orijinal soru ile ilgisizdir ve muhtemelen bir yorum olmalıdır.
Patrick M

22

Sadece estetiği etkilemekle kalmaz, aynı zamanda kod yerleştirmeyi de önler.

Verilerinizin de geçerli olduğundan emin olmak için bir ön koşul olarak işlev görebilir.


18

Bu elbette özneldir, ancak bence iki noktada güçlü bir şekilde gelişir:

  • Artık işlevinizin conditionbekletilirse yapacak hiçbir şeyi kalmayacağı hemen ortadadır .
  • Yuvalama seviyesini düşük tutar. Yuvalamak, okunabilirliği düşündüğünüzden daha fazla incitir.

15

Birden çok dönüş noktası C'de (ve daha az ölçüde C ++) bir problemdi, çünkü sizi her dönüş noktasından önce temizleme kodunu kopyalamaya zorladılar. Çöp toplama ile try| finallyyapı ve usingbloklar, onlardan korkmanız için hiçbir neden yok.

Nihayetinde, sizin ve iş arkadaşlarınızın okumayı daha kolay buldukları şey gelir.


Tek sebep bu değil. Temizlik maddeleri gibi pratik hususlarla hiçbir ilgisi olmayan sahte kodla ilgili akademik bir neden var. Bu akademik neden, zorunlu yapıların temel formlarına saygı duymakla ilgilidir. Aynı şekilde döngü çıkışlarını ortasına koymazsınız. Bu şekilde değişmezler titizlikle tespit edilebilir ve davranışlar kanıtlanabilir. Veya fesih kanıtlanabilir.
v.oddou

1
haberler: aslında erken mola ve geri dönüşlerin kötü olmasının çok pratik bir nedenini buldum ve bu söylediklerimin doğrudan bir sonucudur, statik analiz. buyrun , intel C ++ derleyici kullanım kılavuzu kağıdı: d3f8ykwhia686p.cloudfront.net/1live/intel/… . Anahtar kelime:a loop that is not vectorizable due to a second data-dependent exit
v.oddou

12

Performans açısından, iki yaklaşım arasında gözle görülür bir fark olmayacaktır.

Ancak kodlama performanstan daha fazlasıdır. Netlik ve sürdürülebilirlik de çok önemlidir. Ve performansı etkilemediği gibi durumlarda önemli olan tek şey budur.

Hangi yaklaşımın tercih edileceğine dair rakip düşünce okulları vardır.

Bir görüş, diğerlerinin bahsettiği görüştür: ikinci yaklaşım, kodlama netliğini artıran yuvalama seviyesini azaltır. Bu zorunlu bir tarzda doğaldır: yapacak bir şeyin kalmadıysa, erken dönebilirsin.

Daha işlevsel bir stil açısından başka bir görüş, bir yöntemin sadece bir çıkış noktasına sahip olmasıdır. İşlevsel bir dilde her şey bir ifadedir. Yani eğer ifadelerin her zaman başka bir cümlesi olmalıdır. Aksi takdirde, if ifadesinin her zaman bir değeri olmaz. Yani fonksiyonel tarzda, ilk yaklaşım daha doğal.


11

Koruma hükümleri veya ön koşullar (muhtemelen görebileceğiniz gibi) belirli bir koşulun karşılanıp karşılanmadığını kontrol edin ve ardından programın akışını kırar. Gerçekten sadece bir ififadenin tek bir sonucuyla ilgilendiğiniz yerler için harikadır . Söylemek yerine:

if (something) {
    // a lot of indented code
}

Koşulu tersine çevirirsiniz ve bu ters koşul yerine getirilirse kırılırsınız

if (!something) return false; // or another value to show your other code the function did not execute

// all the code from before, save a lot of tabs

returnkadar kirli değil goto. Kodunuzun geri kalanını işlevin çalışamayacağını göstermek için bir değer iletmenizi sağlar.

Bunun iç içe koşullarda nereye uygulanabileceğine ilişkin en iyi örnekleri göreceksiniz:

if (something) {
    do-something();
    if (something-else) {
        do-another-thing();
    } else {
        do-something-else();
    }
}

vs:

if (!something) return;
do-something();

if (!something-else) return do-something-else();
do-another-thing();

İlkinin daha temiz olduğunu iddia eden birkaç kişi bulacaksınız, ancak elbette tamamen öznel. Bazı programcılar, bir şeyin girintiyle hangi koşulların altında çalıştığını bilmek isterken, yöntem akışını doğrusal tutmayı tercih ederim.

Bir an için precons'un hayatınızı değiştireceğini veya sizi koyacağını önermeyeceğim, ancak kodunuzu okumak biraz daha kolay olabilir.


6
Sanırım o zaman birkaç kişiden biriyim. İlk sürümü okumayı daha kolay buluyorum. Yuvalanmış ifs, karar ağacını daha belirgin hale getirir. Öte yandan, birkaç ön koşul varsa, hepsini işlevin en üstüne koymanın daha iyi olduğunu kabul ediyorum.
Diğerleri

@ Diğer taraf: tamamen katılıyorum. seri şekilde yazılan getiriler beyninizin olası yolları serileştirmesine ihtiyaç duyar. Eğer if ağacı doğrudan bazı geçici mantığa eşlendiğinde, örneğin lisp'e derlenebilir. ancak seri dönüş modası bir derleyicinin yapması çok daha zordur. Buradaki noktanın açıkça bu varsayımsal senaryo ile ilgisi yoktur, kod analizi, optimizasyon şansı, düzeltme kanıtı, sonlandırma kanıtı ve değişmezlerin tespiti ile ilgilidir.
v.oddou

1
Hiç kimse daha fazla yuva ile kod okumayı daha kolay bulamaz. Bir şey gibi ne kadar çok blok varsa, bir bakışta okumak o kadar kolay olur. Burada ilk örnek okumak kolay bir yolu yoktur ve sadece gerçek dünyada böyle kod çoğu derine gider ve her yuva ile daha fazla beyin gücü gerektirir zaman 2 yuva gider.
user441521

1
Eğer iç içe yerleştirme iki kat daha derin olsaydı, "Ne zaman 'başka bir şey yapıyorsun' sorusunu cevaplamanız ne kadar sürer?" Sanırım bir kalem ve kağıt olmadan cevaplamakta zorlanacaksınız. Ama eğer hepsi nöbet tutucuysa, bunu kolayca cevaplayabilirsiniz.
David Storfer

9

Burada birkaç iyi nokta vardır, ancak yöntem çok uzunsa, birden fazla dönüş noktası da okunamaz olabilir . Bununla birlikte, birden fazla dönüş noktası kullanacaksanız, yönteminizin kısa olduğundan emin olun, aksi takdirde birden fazla dönüş noktasının okunabilirlik bonusu kaybedilebilir.


8

Performans iki bölümden oluşmaktadır. Yazılım üretilirken performansınız var, ancak geliştirme ve hata ayıklama sırasında da performans elde etmek istiyorsunuz. Bir geliştiricinin istediği son şey önemsiz bir şey için "beklemektir". Sonunda, bunu optimizasyon etkinken derlemek benzer bir kodla sonuçlanacaktır. Bu yüzden her iki senaryoda da işe yarayan bu küçük hileleri bilmek güzel.

Söz konusu dava açık, ReSharper doğru. ifİfadeleri yerleştirmek ve kodda yeni bir kapsam oluşturmak yerine , yönteminizin başlangıcında açık bir kural belirlersiniz. Okunabilirliği artırır, bakımı daha kolay olur ve nereye gitmek isterse bulmak için elemek zorunda olduğu kuralların miktarını azaltır.


7

Şahsen ben sadece 1 çıkış noktasını tercih ediyorum. Metodlarınızı kısa ve dikkat çekici bir şekilde tutarsanız başarmak kolaydır ve kodunuz üzerinde çalışan bir sonraki kişi için öngörülebilir bir desen sağlar.

Örneğin.

 bool PerformDefaultOperation()
 {
      bool succeeded = false;

      DataStructure defaultParameters;
      if ((defaultParameters = this.GetApplicationDefaults()) != null)
      {
           succeeded = this.DoSomething(defaultParameters);
      }

      return succeeded;
 }

Bu, bir işlevden çıkmadan önce belirli yerel değişkenlerin değerlerini kontrol etmek istiyorsanız da çok kullanışlıdır. Yapmanız gereken tek şey son dönüşe bir kırılma noktası yerleştirmek ve (bir istisna atılmadıkça) vurmanız garanti edilir.


4
bool PerformDefaultOperation () {DataStructure defaultParameters = this.GetApplicationDefaults (); return (defaultParameters! = NULL && this.DoSomething (defaultParameters);} Orada, sizin için
çözdüm

1
Bu örnek çok basittir ve bu modelin amacını gözden kaçırır. Bu işlevin içinde yaklaşık 4 kontrol daha yapın ve sonra bize daha okunabilir olduğunu söyleyin, çünkü değil.
user441521

5

Kodun nasıl göründüğüne dair birçok iyi neden . Peki ya sonuçlar ?

Bazı C # koduna ve IL derlenmiş formuna bir göz atalım:


using System;

public class Test {
    public static void Main(string[] args) {
        if (args.Length == 0) return;
        if ((args.Length+2)/3 == 5) return;
        Console.WriteLine("hey!!!");
    }
}

Bu basit snippet derlenebilir. Oluşturulan .exe dosyasını ildasm ile açabilir ve sonucun ne olduğunu kontrol edebilirsiniz. Tüm birleştirici şeyi göndermeyeceğim ama sonuçları açıklayacağım.

Oluşturulan IL kodu aşağıdakileri yapar:

  1. İlk koşul yanlışsa, ikincisinin bulunduğu koda atlar.
  2. Eğer bu doğruysa son talimata atlar. (Not: son talimat bir dönüştür).
  3. İkinci durumda, sonuç hesaplandıktan sonra aynı şey olur. Karşılaştır ve: yanlışsa Console.WriteLine'a veya bu doğruysa sonuna kadar var.
  4. Mesajı yazdırın ve geri dönün.

Yani kod sonuna kadar atlayacak gibi görünüyor. Yuvalanmış kodla normal yaparsak ne olur?

using System;

public class Test {
    public static void Main(string[] args) {
        if (args.Length != 0 && (args.Length+2)/3 != 5) 
        {
            Console.WriteLine("hey!!!");
        }
    }
}

Sonuçlar IL talimatlarında oldukça benzerdir. Fark şudur ki, koşul başına atlamalar yapılmadan önce: false ise sonraki kod parçasına gider, true ise biter. Ve şimdi IL kodu daha iyi akıyor ve 3 atlama var (derleyici bunu biraz optimize etti): 1. İlk atlama: Uzunluk 0 olduğunda kodun tekrar atladığı (Üçüncü atlama) bir parçaya. 2. İkinci: bir talimattan kaçınmak için ikinci koşulun ortasında. 3. Üçüncüsü: ikinci koşul yanlışsa sonuna kadar atlayın.

Her neyse, program sayacı her zaman atlayacaktır.


5
Bu iyi bir bilgi - ama kişisel olarak IL "daha iyi
akarsa

1
C # derleyicisinin bir sonraki güncellemesinde optimizasyonun bugün gördüğünüzle karşılaştırıldığında nasıl çalışacağını gerçekten tahmin edemezsiniz. Şahsen, herhangi bir yerde sıkı bir döngüde ciddi bir performans probleminiz olduğunu ve başka fikirlerinizin bitmediğini göstermediği sürece, IL'de farklı bir sonuç üretmek için C # kodunu değiştirmeye çalışıyorum. . O zaman bile, diğer seçenekler hakkında daha fazla düşünürdüm. Aynı şekilde, JIT derleyicisinin her platform için kendisine beslenen IL ile ne tür optimizasyonlar yaptığına dair bir fikriniz olduğundan şüpheliyim ...
Craig

5

Teoride, ifdal tahmini isabet oranını arttırırsa ters çevirme daha iyi performans sağlayabilir. Uygulamada, özellikle de derlemeden sonra, şube tahmininin nasıl davranacağını bilmek çok zor olduğunu düşünüyorum, bu yüzden derleme kodunu yazmam dışında günlük gelişimimde bunu yapmayacağım.

Burada şube tahmini hakkında daha fazla bilgi .


4

Bu sadece tartışmalıdır. Erken dönüş konusunda “programcılar arasında bir anlaşma” yoktur. Bildiğim kadarıyla her zaman özneldir.

Bir performans argümanı yapmak mümkündür, çünkü çoğu zaman doğru olmak için yazılı koşullara sahip olmak daha iyidir; bunun daha net olduğu da iddia edilebilir. Öte yandan, iç içe geçmiş testler oluşturur.

Bu soruya kesin bir cevap alacağınızı sanmıyorum.


Performans argümanınızı anlamıyorum. Koşullar boolean, sonuç ne olursa olsun, her zaman iki sonuç vardır ... Bir ifadeyi tersine çevirmenin (veya değiştirmemenin) bir sonucu neden değiştireceğini anlamıyorum. (Duruma "DEĞİL" eklemeyi söylemediğiniz sürece, ölçülebilir miktarda işleme eklenir ...
Oli

2
Optimizasyon şu şekilde çalışır: En yaygın durumun, başka bir blok yerine if bloğunda olduğundan emin olursanız, CPU genellikle boru hattına if bloğundan yüklenmiş ifadeleri içerir. Koşul yanlış döndürürse, CPU'nun boru hattını boşaltması ve ...
Otherside

Ben de diğer tarafın sadece okunabilirlik için söylediklerini yapmaktan hoşlanıyorum, “başka” yerine “o zaman” bloğunda olumsuz durumlardan nefret ediyorum. CPU'nun ne yaptığını bilmeden veya düşünmeden bile bunu yapmak mantıklı
Andrew Bullock

3

Zaten orada çok fazla anlayışlı cevap var, ama yine de, biraz farklı bir duruma yönelirdim: Önkoşul yerine, bu gerçekten bir fonksiyonun üzerine konulmalı, adım adım başlatmayı düşünün, burada başarılı olmak için her adımı kontrol etmeniz ve ardından bir sonraki adımla devam etmeniz gerekir. Bu durumda, üstteki her şeyi kontrol edemezsiniz.

Yuvalama paradigmasını izlediğim için Steinberg ASIOSDK ile bir ASIO ana bilgisayar uygulaması yazarken kodumu gerçekten okunamaz buldum. Sekiz kat derinliğe inmişti ve yukarıda Andrew Bullock'un belirttiği gibi bir tasarım kusuru göremiyorum. Tabii ki, başka bir işleve bazı iç kodları paketleyebilir ve daha sonra okunabilir hale getirmek için orada kalan seviyeleri iç içe olabilirdi, ama bu benim için oldukça rastgele görünüyor.

Yuvaları nöbet cümleleriyle değiştirerek, temizleme kodunun, işlev yerine daha önce yerine çok daha önce ortaya çıkması gereken bir kısmı ile ilgili bir yanlış anlama bile keşfettim. İç içe geçmiş dallarda, bunu asla göremezdim, yanlış anlamama yol açtıklarını bile söyleyebilirdiniz.

Bu nedenle, ters çevrilmiş kodların daha açık bir koda katkıda bulunabileceği başka bir durum olabilir.


3

Birden çok çıkış noktaları kaçınmak olabilir performans kazançları yol açar. C # hakkında emin değilim ama C ++ 'da Adlandırılmış Dönüş Değeri Optimizasyonu (Kopya Elision, ISO C ++ '03 12.8 / 15) tek bir çıkış noktasına sahip olmasına bağlıdır. Bu optimizasyon, dönüş değerinizi oluşturan kopyayı önler (özel örneğinizde önemli değildir). Bu, her işlev çağrıldığında bir yapıcı ve bir yıkıcı kaydettiğiniz için sıkı döngülerde önemli ölçüde performans kazanımlarına yol açabilir.

Ancak, ek kurucu ve yıkıcı çağrılarını saklayan vakaların% 99'u için, iç içe ifblokların getirdiği okunabilirlik kaybına değmez (diğerleri işaret ettiği gibi).


2
Orada. nihayet NRVO söz birini bulmak için aynı şeyi (2 dondurulmuş) soran 3 soru onlarca cevap 3 uzun okuma aldı. gee ... teşekkür ederim.
v.oddou

2

Bu bir görüş meselesi.

Benim normal yaklaşım tek satır ifs önlemek ve bir yöntemin ortasında döndürmek olacaktır.

Yönteminizin her yerinde önerdiği gibi satırlar istemezsiniz, ancak yönteminizin üst kısmında bir grup varsayımı kontrol etmek ve yalnızca hepsi geçerse gerçek işinizi yapmak için söylenecek bir şey vardır.


2

Bence sadece geri dönüşü (ya da hiç kontrol etmeyeceğiniz işe yaramaz bir dönüş kodunu) iade ediyorsanız erken dönüş iyidir ve iç içe geçmekten kaçındığınız ve aynı zamanda işlevinizin açık olduğunu belirlediğiniz için okunabilirliği artırabilir.

Aslında bir returnValue döndürüyorsanız - yuvalama genellikle gitmenin daha iyi bir yoludur, returnValue'unuzu yalnızca bir yerde (sonunda - duh) döndürmenize neden olur ve kodunuzu birçok durumda daha sürdürülebilir hale getirebilir.


1

Emin değilim, ama bence, R # uzak atlamalar kaçınmaya çalışır. IF-ELSE varsa, derleyici böyle bir şey yapar:

Koşul false -> false_condition_label öğesine çok atlama

true_condition_label: yönerge1 ... yönerge_n

false_condition_label: yönerge1 ... yönerge_n

son blok

Koşul doğruysa atlama ve L1 önbellek yok, ancak false_condition_label'e atlamak çok uzak olabilir ve işlemci kendi önbelleğini sunmalıdır. Önbelleği senkronize etmek pahalıdır. R #, kısa atlamaların yerine uzun atlamaları değiştirmeye çalışır ve bu durumda, tüm talimatların zaten önbellekte olması olasılığı daha yüksektir.


0

Bence bu sizin tercih ettiğiniz şeye bağlı, belirtildiği gibi, genel bir anlaşma yok. Rahatsızlığı azaltmak için bu tür bir uyarıyı "İpucu" olarak azaltabilirsiniz.


0

Benim fikrim, "bir fonksiyonun ortasında" geri dönüşünün "öznel" olmaması gerektiğidir. Nedeni oldukça basit, bu kodu alın:

    işlev do_something (data) {

      if (! is_valid_data (veri)) 
            yanlış döndür;


       do_something_that_take_an_hour (veri);

       istance = yeni object_with_very_painful_constructor (veri);

          if (istance geçersiz) {
               hata mesajı( );
                dönüş ;

          }
       connect_to_database ();
       get_some_other_data ();
       dönüş;
    }

Belki ilk "dönüş" çok sezgisel değil, ama bu gerçekten tasarruf. Temiz kodlar hakkında "öznel" kötü fikirlerini kaybetmek için daha fazla uygulamaya ihtiyaç duyan çok fazla "fikir" vardır.


Bir istisna dili ile bu sorunlara sahip değilsiniz.
user441521

0

Bu tür kodlamanın birkaç avantajı vardır, ancak benim için büyük kazanç, hızlı dönebilirseniz, uygulamanızın hızını artırabilirsiniz. IE Önkoşul X nedeniyle bir hata ile hızlı bir şekilde geri dönebileceğini biliyorum. Bu, önce hata durumlarından kurtulur ve kodunuzun karmaşıklığını azaltır. Birçok durumda cpu boru hattı daha temiz olabileceğinden, boru hattı çökmelerini veya anahtarlarını durdurabilir. İkincisi, bir döngüde iseniz, hızlı bir şekilde kırılma veya geri dönme size çok fazla işlemci kazandırabilir. Bazı programcılar bu tür hızlı çıkış yapmak için döngü değişmezlerini kullanırlar, ancak bu işlemde cpu boru hattınızı bozabilir ve hatta bellek arama sorunu yaratabilir ve cpu'nun dış önbellekten yüklenmesi gerekir. Ama temelde istediğini yapmalısın, döngü veya işlev sonlandırılması, sadece doğru kodun bazı soyut kavramını uygulamak için karmaşık bir kod yolu oluşturmaz. Sahip olduğunuz tek alet bir çekiçse, her şey bir çivi gibi görünür.


Graffic'in cevabını okurken, iç içe kod, derleyici tarafından yürütülen kodun birden fazla döndürme kullanmaktan daha hızlı bir şekilde optimize edildiği gibi geliyor. Ancak, birden fazla geri dönüş daha hızlı olsa bile, bu optimizasyon muhtemelen uygulamanızı bu kadar hızlandırmaz ... :)
hangy

1
Kabul etmiyorum - Birinci Kanun algoritmayı doğru bir şekilde belirlemekle ilgilidir. Ancak hepimiz Mühendisleriz, bu da sahip olduğumuz kaynaklarla ilgili doğru sorunu çözmek ve mevcut bütçede yapmaktır. Çok yavaş olan bir uygulama amaca uygun değildir.
David Allan Finch
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.