Bir işlevin yalnızca bir dönüş ifadesi olmalı mı?


780

Bir işlevde yalnızca bir iade ifadesinin bulunmasının daha iyi bir uygulama olmasının iyi nedenleri var mı?

Yoksa mantıksal olarak doğru olur olmaz bir fonksiyondan geri dönmek uygun mudur, yani fonksiyonda birçok dönüş ifadesi olabilir mi?


25
Sorunun dil bilincinde olmadığı konusunda hemfikir değilim. Bazı dillerde, birden fazla getiri elde etmek diğerlerinden daha doğal ve kullanışlıdır. RAII kullanan bir C ++ bir daha bir C fonksiyonunda erken dönüşleri şikayet daha olasıdır.
Adrian McCarthy

3
Bu yakından ilişkilidir ve mükemmel cevapları vardır: programmers.stackexchange.com/questions/118703/…
Tim Schmelter

dil agnostik? İşlevsel dili kullanan birine, işlev başına bir dönüş kullanması gerektiğini açıklayın: p
Boiethios

Yanıtlar:


741

Genellikle "kolay" durumlar için dönmek için bir yöntemin başlangıcında birkaç ifade var. Örneğin, bu:

public void DoStuff(Foo foo)
{
    if (foo != null)
    {
        ...
    }
}

... daha okunabilir hale getirilebilir (IMHO):

public void DoStuff(Foo foo)
{
    if (foo == null) return;

    ...
}

Yani evet, bir işlev / yöntemden birden çok "çıkış noktası" olması iyi olduğunu düşünüyorum.


83
Kabul. Birden fazla çıkış noktasına sahip olmak elden çıkabilse de, tüm fonksiyonunuzu bir IF bloğuna koymaktan daha iyi olduğunu düşünüyorum. Dönüş kodunuzu okunaklı olduğu kadar sık ​​kullanın.
Joshua Carmody

172
Bu "bekçi ifadesi" olarak bilinir, Fowler'in Refactoring'i.
Lars Westergren

12
Fonksiyonlar nispeten kısa tutulduğunda, dönüş noktası ortada olan bir fonksiyonun yapısını takip etmek zor değildir.
KJAWolf

21
If-else ifadelerinin büyük blok, her biri bir dönüş? Bu hiçbirşey. Böyle bir şey genellikle kolayca yeniden düzenlenir. (en azından bir sonuç değişkeni ile daha yaygın tek çıkış varyasyonu, bu yüzden çoklu çıkış varyasyonunun daha zor olacağından şüpheliyim.) Gerçek bir baş ağrısı istiyorsanız, if-else ve while döngülerinin bir kombinasyonuna bakın (yerel tarafından kontrol edilir) booleans) (burada sonuç değişkenleri ayarlanır), yöntemin sonunda bir son çıkış noktasına götürür. Bu çıldırmış tek çıkış fikri ve evet bununla uğraşmak zorunda kaldığım gerçek deneyimlerden bahsediyorum.
Marcus Andrén

7
'Hayal edin:' DoStuff 'fonksiyonunun sonunda "IncreaseStuffCallCounter" yöntemini yapmanız gerekir. Bu durumda ne yapacaksın? :) '-DoStuff() { DoStuffInner(); IncreaseStuffCallCounter(); }
Jim Balter

355

Kimse Code Complete'ten bahsetmedi veya alıntı yapmadı, ben de yapacağım.

17.1 dönüş

Her rutinde dönüş sayısını en aza indirin . Yukarıdaki bir yerde geri dönme olasılığının farkında değilseniz, bir rutini anlamak daha zordur.

Okunabilirliği artırdığında bir dönüş kullanın . Belirli rutinlerde, cevabı öğrendikten sonra, derhal çağıran rutine geri dönmek istersiniz. Rutin herhangi bir temizleme gerektirmeyecek şekilde tanımlanmışsa, hemen geri dönmemesi daha fazla kod yazmanız gerektiği anlamına gelir.


64
"Küçült" nüansı için +1 ancak birden çok dönüşü yasaklamıyor.
Raedwald

13
"anlaşılması daha zor", özellikle yazar genel iddiayı desteklemek için herhangi bir ampirik kanıt sunmadığında çok özneldir ... son iade ifadesi için kodda birçok koşullu konumda ayarlanması gereken tek bir değişkene sahip olmak eşit olarak uygulanır "Değişkenin işlevinin üstünde bir yere
atanma

26
Şahsen, mümkün olduğunca erken dönmeyi tercih ediyorum. Neden? Belirli bir vaka için return anahtar kelimesini gördüğünüzde, anında "Bitirdim" ifadesini bilirsiniz - daha sonra bir şey olursa ne olduğunu anlamak için okumaya devam etmenize gerek yoktur.
Mark Simpson

12
@ HestonT.Holtmann: yapılan şey Kod tam programlama kitapları arasında benzersiz tavsiye olmasıydı edilir ampirik kanıtlarla destek verdi.
Adrian McCarthy

9
Bu muhtemelen kabul edilen cevabı vermelidir, çünkü çoklu dönüş noktalarına sahip olmak her zaman iyi değildir, ancak bazen gerekli olabilir.
Rafid

229

Ben pratikte defalarca yararlı yararlı bulduğum için , aslında birden fazla çıkış noktalarına keyfi olarak karar vermek inanılmaz derecede yanlış olacağını söyleyebilirim , aslında netlik için sık sık mevcut kodu birden fazla çıkış noktalarına yeniden . Bu şekilde iki yaklaşımı karşılaştırabiliriz: -

string fooBar(string s, int? i) {
  string ret = "";
  if(!string.IsNullOrEmpty(s) && i != null) {
    var res = someFunction(s, i);

    bool passed = true;
    foreach(var r in res) {
      if(!r.Passed) {
        passed = false;
        break;
      }
    }

    if(passed) {
      // Rest of code...
    }
  }

  return ret;
}

Birden çok çıkış noktaları koduna karşılaştırın edilir izin: -

string fooBar(string s, int? i) {
  var ret = "";
  if(string.IsNullOrEmpty(s) || i == null) return null;

  var res = someFunction(s, i);

  foreach(var r in res) {
      if(!r.Passed) return null;
  }

  // Rest of code...

  return ret;
}

Bence ikincisi oldukça açık. Birden fazla çıkış noktasının eleştirisini anlatabildiğim kadarıyla bugünlerde oldukça eski bir bakış açısı.


12
Netlik seyircinin gözündedir - bir fonksiyona bakıyorum ve bir başlangıç ​​ve orta bir son arıyorum. İşlev küçük olduğunda - ama bir şeyin neden kırıldığını ve "Kodun Geri Kalanı" nın önemsiz olduğu ortaya çıkmaya çalıştığınızda, ret'in neden olduğunu öğrenmek için yaşları harcayabilirsiniz
Murph

7
Her şeyden önce, bu örnek bir örnek. İkincisi, nerede: string ret; "ikinci versiyona geçelim mi? alt fonksiyonlar?
Rick Minerich

25
@Rick 1. Deneyimlerimden ödün vermedim, bu aslında birçok kez karşılaştığım bir model, 2. 'Kodun geri kalanında' atandı, belki bu açık değil. 3. Ee? Bir örnek mi? 4. Sanırım bu açıdan bu doğru, ama bunu haklı çıkarmak mümkün, 5. yapabilirdi ...
ljs

5
@ Önemli nokta, kodun büyük bir if ifadesine sarılmasından daha erken dönmenin daha kolay olmasıdır. Doğru yeniden düzenleme ile bile, deneyimimde çok şey ortaya çıkıyor.
ljs

5
Birden fazla dönüş ifadesine sahip olmama noktası, hata ayıklamayı kolaylaştırır, çok daha kısa bir sürede okunabilirlik içindir. Sondaki bir kesme noktası, çıkış değerini, istisnaları görmenizi sağlar. Okunabilirlik için gösterilen 2 işlev aynı şekilde davranmaz. ! R.Passed, ancak "daha okunabilir" olanı null döndürmek için bunu değiştirirse, varsayılan dönüş boş bir dizedir. Yazar, daha önce sadece birkaç satırdan sonra bir varsayılanın olduğunu yanlış anladı. Önemsiz örneklerde bile net olmayan varsayılan getirilere sahip olmak kolaydır, sonunda tek bir getiri zorlamaya yardımcı olur.
Mitch

191

Şu anda üzerinde çalışan iki insanın "tek çıkış noktası" teorisine körü körüne abone olduğu bir kod tabanı üzerinde çalışıyorum ve deneyimlerden bunun korkunç korkunç bir uygulama olduğunu söyleyebilirim. Kodun korunmasını son derece zorlaştırır ve size nedenini göstereceğim.

"Tek çıkış noktası" teorisi ile, kaçınılmaz olarak şuna benzer bir kodla sarılırsınız:

function()
{
    HRESULT error = S_OK;

    if(SUCCEEDED(Operation1()))
    {
        if(SUCCEEDED(Operation2()))
        {
            if(SUCCEEDED(Operation3()))
            {
                if(SUCCEEDED(Operation4()))
                {
                }
                else
                {
                    error = OPERATION4FAILED;
                }
            }
            else
            {
                error = OPERATION3FAILED;
            }
        }
        else
        {
            error = OPERATION2FAILED;
        }
    }
    else
    {
        error = OPERATION1FAILED;
    }

    return error;
}

Bu sadece kodu takip etmeyi çok zorlaştırmakla kalmaz, aynı zamanda daha sonra geri dönüp 1 ile 2 arasında bir işlem eklemeniz gerektiğini söyler. if / else koşullarınız ve parantezleriniz doğru şekilde eşleştirilir.

Bu yöntem kod bakımını son derece zor ve hataya açık hale getirir.


5
@Murph: Her koşuldan sonra her birini okumadan başka hiçbir şeyin olmadığını bilmenin hiçbir yolu yoktur. Normalde bu tür konuların öznel olduğunu söyleyebilirim, ama bu kesinlikle yanlış. Her hatanın geri dönüşüyle, işiniz bitti, tam olarak ne olduğunu biliyorsunuz.
GEOCHET

6
@Murph: Bu tür kodların kullanıldığını, kötüye kullanıldığını ve aşırı kullanıldığını gördüm. İçeride doğru bir if / else olmadığı için örnek oldukça basittir. Bu tür kodların parçalanması gereken tek şey unutulmuş bir "başka" dır. AFAIK, bu kodun istisnalara gerçekten kötü ihtiyacı var.
paercebal

15
Bunu yeniden düzenleyebilir, "saflığını" korur: if (! SUCCEEDED (Operation1 ())) {} else error = OPERATION1FAILED; if (hata! = S_OK) {if (SUCCEEDED (Operation2 ())) {} başka hata = OPERATION2FAILED; } if (hata! = S_OK) {if (SUCCEEDED (Operation3 ())) {} başka hata = OPERATION3FAILED; } //vb.
Joe Pineda

6
Bu kodun yalnızca bir çıkış noktası yoktur: bu "error =" ifadelerinin her biri bir çıkış yolundadır. Bu sadece işlevlerden çıkmakla kalmaz, aynı zamanda herhangi bir blok veya diziden çıkmakla da ilgilidir.
Jay Bazuzi

6
Tek dönüşün "kaçınılmaz olarak" derin yuvalama ile sonuçlandığına katılmıyorum. Örneğiniz, tek bir dönüşle (gotos olmadan) basit, doğrusal bir işlev olarak yazılabilir. Kaynak yönetimi için yalnızca RAII'ye güvenmezseniz veya güvenemezseniz, erken iadeler sızıntılara veya yinelenen kodlara neden olur. En önemlisi, erken geri dönüşler, koşul sonrası iddiaların uygulanmasını mümkün kılmaz.
Adrian McCarthy

72

Yapısal programlama , işlev başına yalnızca bir dönüş ifadesine sahip olmanız gerektiğini belirtir. Bu karmaşıklığı sınırlamak içindir. Martin Fowler gibi birçok insan, çoklu dönüş ifadeleriyle fonksiyon yazmanın daha kolay olduğunu savunuyor. Bu argümanı yazdığı klasik yeniden düzenleme kitabında sunar. Diğer tavsiyelerine uyup küçük işlevler yazarsanız bu işe yarar. Bu bakış açısına katılıyorum ve sadece katı yapılandırılmış programlama safları, işlev başına tek dönüş ifadelerine bağlı kalıyor.


44
Yapısal programlama bir şey söylemez. Kendilerini yapılandırılmış programlamanın savunucuları olarak tanımlayan (hepsi değil) bazı insanlar bunu söylüyor.
wnoise

15
"Diğer tavsiyelerine uyup küçük işlevler yazarsanız bu işe yarar." En önemli nokta bu. Küçük fonksiyonlar nadiren birçok dönüş noktasına ihtiyaç duyar.

6
Yorum için @wnoise +1, çok doğru. Tüm "yapı programlama" diyor GOTO kullanmayın.
paxos1977

1
@ceretullis: gerekli olmadıkça. Elbette bu gerekli değildir, ancak C'de yararlı olabilir. Linux çekirdeği bunu kullanır ve iyi nedenlerle. GOTO olarak değerlendirilen ZararlıGOTO , fonksiyon mevcut olsa bile kontrol akışını hareket ettirmek için kullanılması hakkında konuştu . Asla "asla kullanma GOTO" demez .
Esteban Küber

1
"tamamen kodun 'yapısını' görmezden gelmekle ilgilidir." - Hayır, tam tersi. "Kaçınılması gerektiğini söylemek mantıklı" - hayır, değil.
Jim Balter

62

Kent Beck'in Uygulama Desenlerindeki koruma maddelerini tartışırken belirttiği gibi bir rutini yapan gibi, tek bir giriş ve çıkış noktası vardır ...

"aynı rutinde birçok yere girip çıkarken karışıklığı önlemekti. FORTRAN'a veya hangi ifadelerin yürütüldüğünü anlamanın bile çok zor olduğu birçok küresel veriyle yazılmış montaj dili programlarına uygulandığında mantıklıydı .. "Küçük yöntemler ve çoğunlukla yerel verilerle gereksiz yere muhafazakar."

Muhafız cümleleriyle yazılmış bir işlevi, uzun iç içe geçmiş ifadelerden çok daha kolay buluyorum if then else.


Tabii ki, "if-then-else deyimlerinin uzun iç içe bir demet" nöbet cümleciklerine tek alternatif değildir.
Adrian McCarthy

@AdrianMcCarthy daha iyi bir alternatifiniz var mı? Alaycı olmaktan daha yararlı olurdu.
shinzou

@kuhaku: Bu alaycı diyeceğimden emin değilim. Cevap bunun bir ya da durum olduğunu öne sürüyor: ya koruma cümleleri ya da if-then-else uzun iç içe demetleri. Pek çok (en çok?) Programlama dili, koruma maddelerinin yanı sıra bu mantığı çarpanlarına ayırmanın birçok yolunu sunar.
Adrian McCarthy

61

Yan etkisi olmayan bir işlevde, tek bir dönüşten daha fazlasına sahip olmak için iyi bir neden yoktur ve bunları işlevsel bir tarzda yazmalısınız. Yan etkileri olan bir yöntemde, işler daha ardışıktır (zaman indekslidir), bu nedenle, dönüş ifadesini çalıştırmayı durdurmak için bir komut olarak kullanarak zorunlu bir tarzda yazarsınız.

Başka bir deyişle, mümkün olduğunda, bu stili tercih edin

return a > 0 ?
  positively(a):
  negatively(a);

bunun üzerinde

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

Kendinizi birkaç iç içe koşul katmanı yazarken bulursanız, örneğin yüklem listesini kullanarak bunu yeniden düzenlemenin bir yolu olabilir. İfs ve else'lerinizin sözdizimsel olarak birbirinden çok uzakta olduğunu fark ederseniz, bunu daha küçük işlevlere bölmek isteyebilirsiniz. Bir ekran metinden daha fazlasını kapsayan bir koşullu bloğun okunması zordur.

Her dil için geçerli zor ve hızlı bir kural yoktur. Tek bir return ifadesine sahip olmak, kodunuzu iyi yapmaz. Ancak iyi kod, işlevlerinizi bu şekilde yazmanıza izin verme eğiliminde olacaktır.


6
+1 "Eğer ifs ve else'lerinizin sözdizimsel olarak birbirinden çok uzakta olduğunu fark ederseniz, bunu daha küçük fonksiyonlara bölmek isteyebilirsiniz."
Andres Jaan Tack

4
+1, bu bir sorunsa, genellikle tek bir işlevde çok fazla şey yaptığınız anlamına gelir. Bu gerçekten en yüksek oy alan cevap olmadığına dair beni
üzüyor

1
Muhafız ifadelerinin de herhangi bir yan etkisi yoktur, ancak çoğu insan bunları yararlı olarak kabul eder. Bu nedenle, herhangi bir yan etkisi olmasa bile, uygulamayı erken durdurmak için nedenler olabilir. Bu cevap bence konuyu tam olarak ele almıyor.
Maarten Bodewes

@ MaartenBodewes-owlstead Bkz. "Sıkılık ve Tembellik"
Apocalisp

43

RAII veya diğer otomatik bellek yönetimine sahip değilseniz, C ++ için bir takma olan C ++ kodlama standartlarında gördüm, sonra her dönüş için temizlemeniz gerekir, bu da kes ve yapıştır anlamına gelir temizleme veya bir goto (her ikisi de kötü form olarak kabul edilen mantıksal olarak 'son olarak' ile aynıdır). Uygulamalarınız C ++ veya başka bir otomatik bellek sisteminde akıllı işaretçiler ve koleksiyonlar kullanmaksa, bunun güçlü bir nedeni yoktur ve her şey okunabilirlik ve daha çok bir karar çağrısı ile ilgilidir.


Peki, son derece optimize edilmiş kod yazmaya çalışırken silmeleri kopyalamanın daha iyi olduğuna inanıyorum, ancak karmaşık 3d meshleri ​​yazılım gibi!
Grant Peters

1
Buna inanmanıza ne sebep olur? Kayıttan çıkarmada bazı ek yüklerin olduğu zayıf optimizasyona sahip bir derleyiciniz varsa, auto_ptrparalel olarak düz işaretçiler kullanabilirsiniz. İlk etapta optimize edici olmayan bir derleyici ile 'optimize edilmiş' kod yazmak garip olurdu.
Pete Kirkham

Bu kural için ilginç bir istisna oluşturur: Programlama diliniz yöntemin sonunda otomatik olarak çağrılan bir şey içermiyorsa ( Java'da try... gibi finally) ve tek bir kaynakla yapabileceğiniz kaynak bakımı yapmanız gerekir. yöntemin sonunda döndürür. Bunu yapmadan önce, durumdan kurtulmak için kodu yeniden düzenlemeyi ciddi olarak düşünmelisiniz.
Maarten Bodewes

@PeteKirkham temizlik için neden kötü? evet git kötü kullanılabilir, ancak bu özel kullanım kötü değildir.
q126y

1
C ++ 'da @ q126y, RAII'den farklı olarak, bir istisna atıldığında başarısız olur. C dilinde, bu tamamen geçerli bir uygulamadır. Bkz. Stackoverflow.com/questions/379172/use-goto-or-not
Pete Kirkham

40

İşlevin ortasında dönüş ifadelerinin kötü olduğu fikrine yaslanıyorum. İşlevin üst kısmında birkaç koruma maddesi oluşturmak için döndürmeler kullanabilirsiniz ve elbette derleyiciye işlevin sonunda neye geri döneceğini söyleyin, ancak işlevin ortasındaki döndürmeleri kaçırmak kolay olabilir ve işlevi yorumlamayı zorlaştırır.


38

Bir işlevde yalnızca bir iade ifadesinin bulunmasının daha iyi bir uygulama olmasının iyi nedenleri var mı?

Evet , var:

  • Tek çıkış noktası, post-koşullarınızı iddia etmek için mükemmel bir yer sağlar.
  • İşlevin sonunda tek bir dönüşe bir hata ayıklayıcı kesme noktası koymak genellikle yararlıdır.
  • Daha az geri dönüş, daha az karmaşıklık anlamına gelir. Doğrusal kodun anlaşılması genellikle daha kolaydır.
  • Bir işlevi tek bir dönüşe sadeleştirmeye çalışmak karmaşıklığa neden oluyorsa, bu daha küçük, daha genel, anlaşılması kolay işlevlere yeniden odaklanmaya teşvik eder.
  • Yıkıcı içermeyen bir dildeyseniz veya RAII kullanmıyorsanız, tek bir dönüş temizlemeniz gereken yer sayısını azaltır.
  • Bazı diller için tek bir çıkış noktası gerekir (ör. Pascal ve Eiffel).

Soru genellikle birden çok getiri arasında yanlış bir ikilik olarak ortaya çıkar veya if ifadeleri derinden iç içe geçer. Neredeyse her zaman sadece tek bir çıkış noktası ile çok doğrusal (derin yuvalama yok) olan üçüncü bir çözüm vardır.

Güncelleme : Görünüşe göre MISRA yönergeleri tek çıkışı teşvik ediyor .

Açık olmak gerekirse , birden fazla geri dönüşün olmasının her zaman yanlış olduğunu söylemiyorum . Ancak başka türlü eşdeğer çözümler verildiğinde, tek dönüşlü olanı tercih etmek için birçok iyi neden vardır.


2
başka bir iyi sebep, muhtemelen bu günlerde en iyisi, tek bir dönüş bildirimi almak için günlük. bir yönteme günlüğe kaydetme eklemek istiyorsanız, yöntemin döndürdüğünü ileten tek bir günlük ifadesi yerleştirebilirsiniz.
inor

FORTRAN ENTRY ifadesi ne kadar yaygındı? Bkz. Docs.oracle.com/cd/E19957-01/805-4939/6j4m0vn99/index.html . Ve maceracıysanız, yöntemleri AOP ve tavsiye sonrası ile kaydedebilirsiniz
Eric Jablow

1
+1 İlk 2 puan beni ikna etmek için yeterliydi. Bu son ikinci paragrafta. Çok amaçlı içiçe koşulların caydırılmasıyla aynı nedenden ötürü loglama elemanına katılmıyorum, çünkü polimorfizmin OOP'lara getirilmesinin ana nedeni olan tek sorumluluk kuralını kırmayı teşvik ediyorlar.
Francis Rodgers

Ben sadece C # ve Kod Sözleşmeleri ile, hala Contract.Ensuresçoklu dönüş noktaları ile kullanabilirsiniz, çünkü post-condition sorunu bir nonissue eklemek istiyorum .
julealgon

1
@ q126y: gotoOrtak temizleme kodunu almak için kullanıyorsanız , temizleme kodunun returnsonunda bir tane olacak şekilde işlevi muhtemelen basitleştirdiniz . Yani sorunu çözdüğünüzü söyleyebilirsiniz goto, ama diyelim ki bunu basitleştirerek çözdünüz return.
Adrian McCarthy

33

Tek bir çıkış noktasına sahip olmak hata ayıklamada avantaj sağlar, çünkü gerçekte hangi değerin döndürüleceğini görmek için bir işlevin sonunda tek bir kesme noktası ayarlamanıza olanak tanır.


6
BRAVO! Bu nesnel nedenden bahseden tek kişi sizsiniz . Bu yüzden tek çıkış noktalarını birden çok çıkış noktasına tercih ederim. Hata ayıklayıcım herhangi bir çıkış noktasında bir kırılma noktası ayarlayabilirse, muhtemelen birden çok çıkış noktası tercih ederim. Şu anki düşüncem, kodlarında bir hata ayıklayıcı kullanmak zorunda olanların pahasına, birden fazla çıkış noktasını kodlayan insanlar bunu kendi yararları için yapıyorlar (ve evet, kod yazan tüm açık kaynaklı katkıda bulunanlardan bahsediyorum. çoklu çıkış noktaları.)
MikeSchinkel

3
EVET. Üretimde zaman zaman hatalı davranan bir sisteme günlük kodu ekliyorum (burada adım atamıyorum). Önceki kodlayıcı tek çıkış kullansaydı, ÇOK daha kolay olurdu.
Michael Blackburn

3
Doğru, hata ayıklamada yardımcı olur. Ancak pratikte, çoğu durumda çağrı işlevinde, çağrıdan hemen sonra - aynı sonuca etkili bir şekilde kırılma noktası ayarlayabildim. (Ve bu pozisyon elbette çağrı yığınında bulunur.) YMMV.
Foo

Bu, hata ayıklayıcınız bir geri adım veya adım geri dönüş işlevi (ve her hata ayıklayıcıyı bildiğim kadarıyla yapmazsa) sağlamazsa, döndürülen değeri hemen döndükten sonra gösterir . Ancak bir değişkene atanmamışsa, değerin değiştirilmesi biraz zor olabilir.
Mart'ta Maarten Bodewes

7
Uzun bir süre boyunca bir hata ayıklayıcı görmedim, bu, yöntemin "kapatılması" için bir kesme noktası (son, sağ kıvırcık ayraç, diliniz ne olursa olsun) belirlemenize izin vermez ve nerede veya ne olursa olsun bu kesme noktasına vurur , dönüş statemets yöntemde. Ayrıca, işlevinizde yalnızca bir dönüş olsa bile, bu işlevden istisna (açık veya devralınan) çıkamayacağınız anlamına gelmez. Bence bu gerçekten geçerli bir nokta değil.
Scott Gartner

19

Genel olarak bir fonksiyondan sadece bir çıkış noktasına sahip olmaya çalışırım. Bununla birlikte, bunu yapmak, aslında gerekenden daha karmaşık bir işlev gövdesi yaratmanın zamanları vardır, bu durumda birden fazla çıkış noktasına sahip olmak daha iyidir. Gerçekte ortaya çıkan karmaşıklığa dayanan bir "karar çağrısı" olmak zorundadır, ancak amaç karmaşıklık ve anlaşılabilirlikten ödün vermeden mümkün olduğunca az çıkış noktası olmalıdır.


"Genel olarak bir fonksiyondan sadece bir çıkış noktasına sahip olmaya çalışıyorum" - neden? "hedef mümkün olduğunca az çıkış noktası olmalı" - neden? Ve 19 kişi neden bu cevabı vermedi?
Jim Balter

@JimBalter Nihayetinde kişisel tercihinize bağlı. Daha fazla çıkış noktası genellikle daha karmaşık bir yönteme (her zaman olmasa da) yol açar ve birinin anlamasını zorlaştırır.
Scott Dorman

"kişisel tercihlere dayanır." - Başka bir deyişle, bir sebep sunamazsınız. "Daha fazla çıkış noktası tipik olarak daha karmaşık bir yönteme yol açar (her zaman olmasa da)" - Hayır, aslında öyle değil. Mantıksal olarak eşdeğer iki fonksiyon göz önüne alındığında, biri nöbet yan tümceleri ve diğeri tek çıkışlı olmak üzere, ikincisi daha yüksek siklomatik karmaşıklığa sahip olacaktır, bu da çok sayıda çalışmanın kodda daha fazla hataya eğilimli ve anlaşılması zor olan sonuçları gösterecektir. Buradaki diğer yanıtları okumaktan faydalanırsınız.
Jim Balter

14

Hayır, çünkü 1970'lerde artık yaşamıyoruz . İşleviniz birden fazla geri dönüşün sorun olacağı kadar uzunsa, çok uzun olur.

(İstisnalar dışında bir dilde herhangi bir çok satırlı işlevin zaten birden çok çıkış noktasına sahip olması gerçeği dışında.)


14

Bir şeyleri gerçekten karmaşık hale getirmediği sürece tercihim tek çıkış olacaktır. Bazı durumlarda, birden fazla mevcut noktanın diğer daha önemli tasarım sorunlarını maskeleyebileceğini buldum:

public void DoStuff(Foo foo)
{
    if (foo == null) return;
}

Bu kodu görünce, hemen sorardım:

  • 'Foo' hiç boş mu?
  • Eğer öyleyse, kaç tane 'DoStuff' müşterisi fonksiyonu boş bir 'foo' ile çağırır?

Bu soruların cevaplarına bağlı olarak,

  1. kontrol asla doğru olmadığı için anlamsızdır (yani bir iddia olmalıdır)
  2. kontrol çok nadiren doğrudur ve bu nedenle, muhtemelen başka bir işlem yapmaları gerektiği için bu belirli arayan işlevlerini değiştirmek daha iyi olabilir.

Yukarıdaki her iki durumda da, kod 'foo'nun asla boş olmamasını ve ilgili arayanların değişmesini sağlamak için muhtemelen bir iddia ile yeniden çalışılabilir.

Birden fazla var aslında negatif bir olabilir iki diğer nedenleri (belirli düşünüyorum C ++ kodu) etkisi . Kod boyutu ve derleyici optimizasyonlarıdır.

Bir işlevin çıkışındaki kapsamda olmayan bir POD C ++ nesnesi, yıkıcısını çağırır. Birkaç geri dönüş ifadesi olduğunda, kapsamda farklı nesneler olabilir ve bu nedenle çağrılacak yıkıcıların listesi farklı olacaktır. Bu nedenle derleyicinin her bir return ifadesi için kod üretmesi gerekir:

void foo (int i, int j) {
  A a;
  if (i > 0) {
     B b;
     return ;   // Call dtor for 'b' followed by 'a'
  }
  if (i == j) {
     C c;
     B b;
     return ;   // Call dtor for 'b', 'c' and then 'a'
  }
  return 'a'    // Call dtor for 'a'
}

Kod boyutu bir sorunsa - bu kaçınılması gereken bir şey olabilir.

Diğer konu "Adlandırılmış Dönüş Değeri Optimizasyonu" (Kopya Elision, ISO C ++ '03 12.8 / 15) ile ilgilidir. C ++, bir uygulamanın aşağıdaki durumlarda kopyalama yapıcısını çağırmayı atlamasına izin verir:

A foo () {
  A a1;
  // do something
  return a1;
}

void bar () {
  A a2 ( foo() );
}

Kodu olduğu gibi alırsak, 'a1' nesnesi 'foo' içinde oluşturulur ve daha sonra kopya yapısı 'a2' oluşturmak için çağrılır. Bununla birlikte, kopya seçimi derleyicinin yığın üzerinde 'a2' ile aynı yerde 'a1' oluşturmasına izin verir. Bu nedenle, işlev geri döndüğünde nesneyi "kopyalamaya" gerek yoktur.

Birden çok çıkış noktası, derleyicinin bunu algılamaya çalışmasını karmaşıklaştırır ve en azından VC ++ 'ın nispeten yeni bir sürümü için, en iyileştirme, işlev gövdesinin birden fazla dönüşü olduğu yerde gerçekleşmedi. Daha fazla bilgi için bkz. Visual C ++ 2005'te Adlandırılmış Dönüş Değeri Optimizasyonu .


1
C ++ örneğinizin son dtor'u dışında tümünü alırsanız, B ve daha sonra C ve B'yi yok etmek için kodun if ifadesinin kapsamı sona erdiğinde yine de üretilmesi gerekir, bu nedenle gerçekten birden fazla döndürme ile hiçbir şey kazanmazsınız. .
Tutulma

4
+1 Ve listenin altında waaaaay Bu kodlama pratiği var GERÇEK nedeni var - NRVO. Ancak, bu bir mikro optimizasyon; ve tüm mikro-optimizasyon uygulamaları gibi, muhtemelen 300 kHz PDP-8 üzerinde programlama için kullanılan ve temiz ve yapılandırılmış kodun önemini anlamayan 50 yaşındaki bir "uzman" tarafından başlatılmıştır. Genel olarak, Chris S'nin tavsiyelerini alın ve mantıklı olduğunda birden çok iade ifadesi kullanın.
BlueRaja - Danny Pflughoeft

Tercihinize katılmıyorum (aklımda, Assert öneriniz aynı zamanda bir dönüş noktasıdır, throw new ArgumentNullException()bu durumda C #'da olduğu gibi), diğer düşüncelerinizi gerçekten beğendim, hepsi benim için geçerli ve bazılarında kritik olabilir niş bağlamları.
julealgon

Bu saman adamlarla dolu bir takoz. Neden footest edileceği sorusunun konu ile ilgisi yoktur, ki bu if (foo == NULL) return; dowork; ya daif (foo != NULL) { dowork; }
Jim Balter

11

Tek bir çıkış noktasına sahip olmak, Siklomatik Karmaşıklığı azaltır ve bu nedenle teoride , değiştirdiğinizde kodunuza hata girme olasılığını azaltır. Ancak uygulama, daha pragmatik bir yaklaşıma ihtiyaç olduğunu öne sürmektedir. Bu nedenle tek bir çıkış noktasına sahip olma eğilimindedir, ancak kodumun daha okunabilir olması durumunda birkaç tane olmasına izin veririm.


Çok anlayışlı. Her ne kadar, bir programcı birden fazla çıkış noktasını ne zaman kullanacağını bilinceye kadar, bir taneyle sınırlandırılması gerektiğini hissediyorum.
Rick Minerich

5
Pek sayılmaz. "If (...) return; ... return;" nin siklomatik karmaşıklığı "if (...) {...} dönüşü" ile aynıdır; Her ikisinin de iki yolu vardır.
Steve Emmerson

11

Kendimi sadece bir returnifade kullanmaya zorladım, çünkü bir anlamda kod kokusu üretecek. Açıklamama izin ver:

function isCorrect($param1, $param2, $param3) {
    $toret = false;
    if ($param1 != $param2) {
        if ($param1 == ($param3 * 2)) {
            if ($param2 == ($param3 / 3)) {
                $toret = true;
            } else {
                $error = 'Error 3';
            }
        } else {
            $error = 'Error 2';
        }
    } else {
        $error = 'Error 1';
    }
    return $toret;
}

(Koşullar keyfi ...)

Daha fazla koşul, fonksiyon büyüdükçe, okuması daha zor olur. Kod kokusuna uyum sağladıysanız, bunu fark edersiniz ve kodu yeniden düzenlemek istersiniz. İki olası çözüm:

  • Birden fazla geri dönüş
  • Ayrı fonksiyonlara yeniden düzenleme

Çoklu İade

function isCorrect($param1, $param2, $param3) {
    if ($param1 == $param2)       { $error = 'Error 1'; return false; }
    if ($param1 != ($param3 * 2)) { $error = 'Error 2'; return false; }
    if ($param2 != ($param3 / 3)) { $error = 'Error 3'; return false; }
    return true;
}

Ayrı Fonksiyonlar

function isEqual($param1, $param2) {
    return $param1 == $param2;
}

function isDouble($param1, $param2) {
    return $param1 == ($param2 * 2);
}

function isThird($param1, $param2) {
    return $param1 == ($param2 / 3);
}

function isCorrect($param1, $param2, $param3) {
    return !isEqual($param1, $param2)
        && isDouble($param1, $param3)
        && isThird($param2, $param3);
}

Verilen, daha uzun ve biraz dağınık, ancak işlevi bu şekilde yeniden düzenleme sürecinde,

  • bir dizi yeniden kullanılabilir fonksiyon yarattı,
  • işlevi daha insan tarafından okunabilir hale getirdi ve
  • fonksiyonların odak noktası değerlerin neden doğru olduğudur.

5
-1: Kötü örnek. Hata mesajı işlemeyi atladınız. Buna ihtiyaç duyulmazsa, isCorrect dönüş xx && yy && zz; burada xx, yy ve z isEqual, isDouble ve isThird ifadeleridir.
kauppi

10

İstediğiniz kadar veya kodu daha temiz yapan herhangi biri olması gerektiğini söyleyebilirim ( bekçi hükümleri gibi) .

Şahsen hiç "en iyi uygulamaları" duymadım / görmedim sadece tek bir iade beyanınız olmalı.

Çoğunlukla, bir mantık yoluna dayanarak mümkün olan en kısa sürede bir işlevden çıkma eğilimindeyim (koruma cümleleri bunun mükemmel bir örneğidir).


10

Birden fazla dönüş genellikle iyi olduğuna inanıyorum (C # yazdığınız kodda). Tek dönüşlü stil C'nin bir dayanak noktasıdır. Ama muhtemelen C kodlamıyorsunuzdur.

Tüm programlama dillerinde bir yöntem için yalnızca bir çıkış noktası gerektiren bir yasa yoktur . Bazı insanlar bu tarzın üstünlüğü konusunda ısrar ederler ve bazen bunu bir "kural" ya da "yasaya" yükseltirler, ancak bu inanç herhangi bir kanıt ya da araştırma ile desteklenmez.

Birden fazla dönüş stili, kaynakların açıkça ayrılması gereken C kodunda kötü bir alışkanlık olabilir, ancak Java, C #, Python veya JavaScript gibi otomatik çöp toplama ve try..finallybloklar (veusing C # ) ve bu argüman geçerli değildir - bu dillerde, merkezi el ile kaynak aktarımına ihtiyaç duyulması çok nadirdir.

Tek bir geri dönüşün daha okunabilir olduğu durumlar ve bunun olmadığı durumlar vardır. Kod satır sayısını azaltıp azaltmadığını, mantığı daha açık hale getirip getirmediğini veya parantez ve girintilerin veya geçici değişkenlerin sayısını azalttığını görün.

Bu nedenle, sanatsal duyarlılıklarınıza uygun sayıda getiri kullanın, çünkü bu teknik değil, bir düzen ve okunabilirlik sorunudur.

Bunu blogumda daha uzun süre konuştum .


10

Sonuçta kaçınılmaz olan "ok" programlamasında söylenecek kötü şeyler olduğu gibi, tek bir çıkış noktasına sahip olmak hakkında söylenecek iyi şeyler vardır .

Giriş doğrulaması veya kaynak tahsisi sırasında birden fazla çıkış noktası kullanıyorsanız, tüm 'hata çıkışlarını' fonksiyonun en üstüne koymaya çalışıyorum.

Hem "SSDSLPedia" nın Spartan Programlama makalesinde ve "Portland Pattern Repository's Wiki" nin tek işlevli çıkış noktası makalesinde bununla ilgili bazı tartışmalar vardır. Ayrıca, elbette, dikkate alınması gereken bu yazı var.

Örneğin, kaynakları tek bir yerde serbest bırakmak için tek bir çıkış noktası (istisna etkin olmayan herhangi bir dilde) istiyorsanız, goto'nun dikkatli uygulamasının iyi olduğunu düşünüyorum; örneğin bu oldukça tutarlı örneğe bakın (ekran gayrimenkulünü kaydetmek için sıkıştırılmış):

int f(int y) {
    int value = -1;
    void *data = NULL;

    if (y < 0)
        goto clean;

    if ((data = malloc(123)) == NULL)
        goto clean;

    /* More code */

    value = 1;
clean:
   free(data);
   return value;
}

Şahsen ben genel olarak, ok programlamayı birden fazla çıkış noktasından hoşlanmadığımdan daha fazla sevmiyorum, ancak her ikisi de doğru uygulandığında faydalıdır. En iyisi, elbette, programınızı hiçbirini gerektirmeyecek şekilde yapılandırmaktır. İşlevinizi birden fazla parçaya bölmek genellikle yardımcı olur :)

Bunu yaparken, bu örnekte olduğu gibi yine de birden fazla çıkış noktasıyla sonuçlandığımı görüyorum, burada bazı büyük işlevler birkaç küçük işleve ayrıldı:

int g(int y) {
  value = 0;

  if ((value = g0(y, value)) == -1)
    return -1;

  if ((value = g1(y, value)) == -1)
    return -1;

  return g2(y, value);
}

Projeye veya kodlama kurallarına bağlı olarak, kazan plakası kodunun çoğu makrolarla değiştirilebilir. Bir yan not olarak, bu şekilde parçalanması g0, g1, g2 fonksiyonlarının ayrı ayrı test edilmesini kolaylaştırır.

Açıkçası, bir OO ve istisna özellikli bir dilde, if-ifadelerini bu şekilde kullanmam (ya da yeterince çabayla kurtulabilseydim) ve kod çok daha basit olurdu. Ve oksuz. Ve nihai olmayan getirilerin çoğu muhtemelen istisna olacaktır.

Kısacası;

  • Birkaç geri dönüş birçok geri dönüşten daha iyidir
  • Birden fazla dönüş büyük oklardan ve koruma maddelerinden daha iyidir genellikle iyidir.
  • İstisnalar, mümkünse çoğu 'koruyucu maddenin' yerine geçebilir / gelmelidir.

NULL işaretçisini serbest bırakmaya çalıştığından örnek y <0 için çöküyor ;-)
Erich Kitzmueller

2
opengroup.org/onlinepubs/009695399/functions/free.html "ptr bir boş gösterici ise hiçbir işlem yapılmaz."
Henrik Gustafsson

1
Hayır, çökmeyecektir, çünkü NULL değerini serbest bırakmak tanımlanmış bir hayır işlemidir. İlk önce NULL için test etmeniz gereken sıkıcı bir yanlış kanıdır.
hlovdal

"Ok" düzeni kaçınılmaz bir alternatif değildir. Bu yanlış bir ikilik.
Adrian McCarthy

9

Atasözünü biliyorsunuz - güzellik bakanın gözündedir .

Bazı insanlar yemin NetBeans ve bazı IntelliJ IDEA , bazıları tarafından Python tarafından ve bazı PHP .

Bazı mağazalarda bunu yapmakta ısrar ederseniz işinizi kaybedebilirsiniz:

public void hello()
{
   if (....)
   {
      ....
   }
}

Soru tamamen görünürlük ve sürdürülebilirlikle ilgilidir.

Devlet makinelerinin mantığını ve kullanımını azaltmak ve basitleştirmek için boolean cebiri kullanmaya bağımlıyım. Bununla birlikte, kodlamada "matematiksel teknikler" kullanmamın uygun olmadığına inanan geçmiş meslektaşları vardı, çünkü görünür ve sürdürülemezdi. Ve bu kötü bir uygulama olurdu. Üzgünüm insanlar, benim kullandığım teknikler benim için çok görünür ve sürdürülebilir - çünkü altı ay sonra koda döndüğümde, kodu açıkça bir atasöz spagetti dağınıklığı gördüğünü anlıyorum.

Hey dostum (eski bir müşterinin söylediği gibi), düzeltmeniz gerektiğinde nasıl düzelteceğinizi bildiğiniz sürece istediğiniz şeyi yapın.

20 yıl önce hatırlıyorum, bir meslektaşım bugün çevik kalkınma stratejisi olarak adlandırılacak olanı kullanmaktan kovuldu . Çok titiz bir planı vardı. Ama menajeri ona "Kullanıcılara kademeli olarak özellikler bırakamazsınız! Şelaleye bağlı kalmalısınız." ." Yöneticiye verdiği yanıt, artan gelişimin müşterinin ihtiyaçlarına daha hassas olacağıydı. Müşterilerin ihtiyaçları için gelişmeye inanıyordu, ancak yönetici "müşterinin ihtiyacını" kodlamaya inanıyordu.

Veri normalizasyonu, MVP ve MVC sınırlarını aşmaktan sık sık suçluyuz . Bir işlev inşa etmek yerine satır içi yapıyoruz. Kısayollar alıyoruz.

Şahsen, PHP'nin kötü uygulama olduğuna inanıyorum, ama ne biliyorum. Tüm teorik argümanlar bir dizi kuralı yerine getirmeye çalışmakla sınırlıdır

kalite = hassasiyet, sürdürülebilirlik ve karlılık.

Diğer tüm kurallar arka planda kaybolur. Ve elbette bu kural asla kaybolmaz:

Tembellik iyi bir programcının erdemidir.


1
"Hey dostum (eski bir müşterinin söylediği gibi), düzeltmeniz gerektiğinde nasıl düzelteceğinizi bildiğiniz sürece istediğiniz şeyi yapın." Sorun: Bunu 'düzelten' genellikle siz değilsiniz.
Dan Barron

Bu cevaba + 1'leyin çünkü nereye gittiğinize katılıyorum, ancak oraya nasıl ulaşacağınız değil. Ben anlayış düzeylerinin olduğunu iddia ediyorum. yani A çalışanının 5 yıllık programlama tecrübesi ve 5 yıl sonra bir şirket ile sahip olduğu anlayışı, bir şirketle yeni başlayan yeni bir üniversite mezunu olan B çalışanından çok farklıdır. Demek istediğim, eğer A çalışanı kodu anlayabilen tek kişi ise, sürdürülemez DEĞİLDİR ve bu yüzden hepimiz B çalışanının anlayabileceği bir kod yazmaya çalışmalıyız. Gerçek sanatın yazılımda olduğu yer burasıdır.
Francis Rodgers

9

Ben erken dönmek ve başka bir yöntemin sonunda çıkmak için nöbet yan tümceleri kullanmaya eğilimli. Tek giriş ve çıkış kuralı tarihsel öneme sahiptir ve özellikle birden çok döndürme (ve birçok kusur) içeren tek bir C ++ yöntemi için 10 A4 sayfaya kadar koşan eski kodla uğraşırken yararlı olmuştur. Daha yakın zamanlarda, kabul edilen iyi uygulama yöntemleri küçük tutmaktır, bu da çoklu çıkışları anlama empedansından daha az emektir. Yukarıdan kopyalanan aşağıdaki Kronoz örneğinde, soru // Kodun geri kalanı ... ?

void string fooBar(string s, int? i) {

  if(string.IsNullOrEmpty(s) || i == null) return null;

  var res = someFunction(s, i);

  foreach(var r in res) {
      if(!r.Passed) return null;
  }

  // Rest of code...

  return ret;
}

Örneğin bir şekilde yapıldığının farkındayım, ancak foreach döngü sonra bir nöbet yan tümcesi kabul edilebilir bir LINQ deyimi içine . Yine, bir örnekte, kodun amacı belli değildir ve someFunction () başka bir yan etkiye sahip olabilir veya sonuç // Kodun geri kalanı ... 'nda kullanılabilir .

if (string.IsNullOrEmpty(s) || i == null) return null;
if (someFunction(s, i).Any(r => !r.Passed)) return null;

Aşağıdaki yeniden düzenlenmiş işlevi vermek:

void string fooBar(string s, int? i) {

  if (string.IsNullOrEmpty(s) || i == null) return null;
  if (someFunction(s, i).Any(r => !r.Passed)) return null;

  // Rest of code...

  return ret;
}

C ++ 'nın istisnaları yok mu? O zaman nullargümanın kabul edilmediğini belirten bir istisna atmak yerine neden geri dönesin?
Maarten Bodewes

1
Belirttiğim gibi, örnek kod önceki bir yanıttan kopyalanır ( stackoverflow.com/a/36729/132599 ). Orijinal örnek null döndürdü ve argüman istisnaları atmak için yeniden düzenleme, yapmaya çalıştığım noktaya veya orijinal soruya maddi değildi. İyi bir uygulama olarak, evet ben normalde (C #) bir boş cümle yerine bir nöbet yan tümcesi ArgumentNullException atmak istiyorum.
David Clarke

7

Düşünebileceğim iyi bir neden, kod bakımı için: tek bir çıkış noktanız var. Sonucun biçimini değiştirmek istiyorsanız, ..., uygulanması çok daha kolaydır. Ayrıca, hata ayıklama için, sadece bir kesme noktası yapıştırabilirsiniz :)

Bunu söyledikten sonra, bir zamanlar kodlama standartlarının 'işlev başına bir dönüş ifadesi' uyguladığı bir kütüphanede çalışmak zorunda kaldım ve oldukça zor buldum. Çok sayıda sayısal hesaplama kodu yazıyorum ve genellikle 'özel durumlar' var, bu yüzden kodu takip etmek oldukça zor oldu ...


Bu gerçekten bir fark yaratmıyor. Döndürülen yerel değişkenin türünü değiştirirseniz, o yerel değişkene yapılan tüm atamaları düzeltmeniz gerekir. Ve yine de farklı bir imzayla bir yöntem tanımlamak daha iyidir, çünkü tüm yöntem çağrılarını da düzeltmeniz gerekecektir.
Maarten Bodewes

@ MaartenBodewes-owlstead - Bir fark yaratabilir. Tüm değişkenleri yerel değişkene düzeltmek veya yöntem çağrılarını değiştirmek zorunda kalmayacağınız yalnızca iki örnek adlandırmak için, işlev bir tarihi dize olarak döndürebilir (yerel değişken, yalnızca dize olarak biçimlendirilmiş gerçek bir tarih olacaktır son anı) veya ondalık sayı döndürebilir ve ondalık basamak sayısını değiştirmek isteyebilirsiniz.
nnnnnn

@nnnnnn Tamam, çıktıda post-işlem yapmak istiyorsanız ... Ama ben sadece post-işlem yapan yeni bir yöntem oluşturmak ve eskisini yalnız bırakmak istiyorum. Bu , refactor için biraz daha zor, ancak diğer çağrıların yeni formatla uyumlu olup olmadığını kontrol etmeniz gerekecek. Ama bu hiç de geçerli olmayan bir sebep.
Maarten Bodewes

7

Birden çok çıkış noktası, yeterince küçük işlevler için iyidir - yani, bir ekran uzunluğunda bütünüyle görüntülenebilen bir işlevdir. Uzun bir fonksiyon da aynı şekilde birden fazla çıkış noktası içeriyorsa, fonksiyonun daha da kesilebileceğinin bir işaretidir.

Kesinlikle gerekli olmadıkça çoklu çıkış işlevlerinden kaçındım dedi . Daha karmaşık fonksiyonlarda bazı belirsiz çizgilerde biraz geri dönüş nedeniyle oluşan hataların ağrısını hissettim.


6

Size tek bir çıkış yolunu zorlayan korkunç kodlama standartlarıyla çalıştım ve işlev önemsiz bir şeyse sonuç neredeyse her zaman yapılandırılmamış spagetti.


ifBaşarıyı döndüren veya döndürmeyen her yöntem çağrısının önünde ifadeyi atlamak için aklınıza söylemekten bahsetmekten bahsetmiyorum bile :(
Maarten Bodewes

6

Tek çıkış noktası - diğer her şey eşit - kodu önemli ölçüde daha okunabilir hale getirir. Ama bir sorun var: popüler inşaat

resulttype res;
if if if...
return res;

sahte, "res =" "dönüş" ten çok daha iyi değil. Tek bir return ifadesine sahiptir, ancak fonksiyonun gerçekten sona erdiği birden fazla nokta vardır.

Birden fazla döndürme (veya "res =" s) işleviniz varsa, bunu tek çıkış noktası olan daha küçük işlevlere bölmek genellikle iyi bir fikirdir.


6

Her zamanki politikam, kodun karmaşıklığı daha fazla eklenerek büyük ölçüde azaltılmadıkça, bir işlevin sonunda yalnızca bir dönüş ifadesine sahip olmaktır. Aslında, daha çok bir dönüş ifadesi olmadan tek bir dönüş kuralını uygulayan Eiffel hayranıyım (sonucunuzu koymak için sadece otomatik olarak oluşturulan bir 'sonuç' değişkeni var).

Kodun, onlarsız açık sürümden daha fazla dönüşle daha net hale getirilebileceği durumlar kesinlikle vardır. Birden fazla dönüş ifadesi olmadan anlaşılamayacak kadar karmaşık bir işleve sahipseniz, daha fazla yeniden işleme ihtiyaç olduğu iddia edilebilir, ancak bazen bu tür şeyler hakkında pragmatik olmak iyidir.


5

Birkaç döndürmeyle sonuçlanırsanız, kodunuzla ilgili bir sorun olabilir. Aksi takdirde, bazen kodu daha temiz hale getirdiğinde, bazen bir altyordamda birden fazla yerden geri dönmek mümkün olduğunu kabul ediyorum.

Perl 6: Kötü Örnek

sub Int_to_String( Int i ){
  given( i ){
    when 0 { return "zero" }
    when 1 { return "one" }
    when 2 { return "two" }
    when 3 { return "three" }
    when 4 { return "four" }
    ...
    default { return undef }
  }
}

böyle daha iyi yazılır

Perl 6: İyi Örnek

@Int_to_String = qw{
  zero
  one
  two
  three
  four
  ...
}
sub Int_to_String( Int i ){
  return undef if i < 0;
  return undef unless i < @Int_to_String.length;
  return @Int_to_String[i]
}

Bunun sadece kısa bir örnek olduğunu unutmayın


Tamam Bu neden oylandı? Bu bir görüş değil gibi değil.
Brad Gilbert

5

Sonunda bir rehber olarak Tek dönüş için oy veriyorum. Bu, ortak bir kod temizleme işlemine yardımcı olur ... Örneğin, aşağıdaki koda bir göz atın ...

void ProcessMyFile (char *szFileName)
{
   FILE *fp = NULL;
   char *pbyBuffer = NULL:

   do {

      fp = fopen (szFileName, "r");

      if (NULL == fp) {

         break;
      }

      pbyBuffer = malloc (__SOME__SIZE___);

      if (NULL == pbyBuffer) {

         break;
      }

      /*** Do some processing with file ***/

   } while (0);

   if (pbyBuffer) {

      free (pbyBuffer);
   }

   if (fp) {

      fclose (fp);
   }
}

Tek dönüşlü C kodu için oy kullanıyorsunuz. Peki ya Çöp toplama ve deneyin .. son olarak blokları olan bir dilde kodlama olsaydı?
Anthony

4

Bu muhtemelen alışılmadık bir perspektiftir, ancak bence birden fazla dönüş ifadesinin tercih edileceğine inanan herkes, sadece 4 donanım kesme noktasını destekleyen bir mikroişlemcide bir hata ayıklayıcı kullanmak zorunda kalmamıştır. ;-)

"Ok kodu" sorunları tamamen doğru olsa da, birden çok dönüş deyimi kullanırken gider gibi görünüyor bir hata ayıklayıcı kullandığınız durumdadır. Çıkışı ve dolayısıyla dönüş koşulunu göreceğinizi garanti etmek için bir kesme noktası koymak için uygun bir tümünü yakalama konumunuz yoktur.


5
Bu sadece farklı türde bir erken optimizasyon. Özel durum için asla optimizasyon yapmamalısınız. Kodun belirli bir bölümünü çok fazla hata ayıklarken bulursanız, kaç çıkış noktasından daha yanlış olduğunu görebilirsiniz.
Kama

Hata ayıklayıcınıza da bağlıdır.
Maarten Bodewes

4

Bir işlevde ne kadar fazla getiri ifadesi varsa, o yöntemdeki karmaşıklık o kadar yüksek olur. Kendinize çok fazla dönüş ifadeniz olup olmadığını merak ediyorsanız, bu işlevde çok fazla kod satırınız olup olmadığını kendinize sormak isteyebilirsiniz.

Ancak, bir / birçok dönüş ifadesinde yanlış bir şey yoktur. Bazı dillerde, diğerlerinden (C) daha iyi bir uygulamadır (C ++).

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.