Koşullu şartlardaki atamalar kötü bir uygulama mıdır?


35

Diyelim ki C'deki iki dizgiyi birleştiren bir işlev yazmak istiyorum. Yazacağım şekilde:

void concat(char s[], char t[]){
    int i = 0;
    int j = 0;

    while (s[i] != '\0'){
        i++;
    }

    while (t[j] != '\0'){
        s[i] = t[j];
        i++;
        j++;
    }

    s[i] = '\0';

}

Bununla birlikte, K & R kitaplarında özellikle farklı süre döngüsünün koşulu dahil olmak üzere kitapta farklı şekilde uygulanmıştır:

void concat(char s[], char t[]){
    int i, j;
    i = j = 0;
    while (s[i] != '\0') i++;

    while ((s[i++]=t[j++]) != '\0');

}

Hangi yol tercih edilir? K & R'nin yaptığı gibi kod yazması teşvik edilir mi yoksa önerilmez mi? Sürümümün diğer insanlar tarafından okunmasının daha kolay olacağını düşünüyorum.


38
Unutma, K&R ilk olarak 1978'de yayınlandı. O zamandan beri kodlama biçimimizde birkaç küçük değişiklik oldu.
corsiKa

28
Teleprinters ve çizgi odaklı editörlerin günlerinde okunabilirlik çok farklıydı. Bunların hepsini tek bir satırda ezmek, daha okunaklıydı.
user2357112, 1

15
Şunun gibi bir şey yerine '\ 0' ile endeksleri ve karşılaştırmaları var while (*s++ = *t++); (Şokum çok paslı, operatör önceliği için orada ebeveynlere ihtiyacım var mı?) K & R kitaplarının yeni bir versiyonunu yayınladı mı? Orijinal kitaplarının çok özlü ve deyimsel kodu vardı.
user949300,

4
Okunabilirlik çok kişisel bir şey - teletypes günlerinde bile. Farklı insanlar farklı tarzları tercih eder. Çok fazla talimatın çarpması kod oluşturma ile ilgiliydi. O günlerde, bazı talimat setleri (örn. Genel Veri), birkaç işlemi tek bir talimatta toplayabilir. Ayrıca, 80'li yılların başında, parantez kullanarak daha fazla talimat üretilen bir efsane vardı. Kod gözden geçiricisine bir efsane olduğunu kanıtlamak için assembler oluşturmak zorunda kaldım.
kupa

10
İki kod bloğunun eşdeğer olmadığını unutmayın. İlk kod bloğu sonlanan kopyalamaz '\0'dan t( whileilk çıkışlarında). Bu, sonuçlanan sdizgiyi sonlandırmadan bırakır '\0'(bellek konumu zaten sıfırlanmadıysa). İkinci kod bloğu '\0', whiledöngüden çıkmadan önce sonlandırmanın kopyasını alacaktır .
Makyen

Yanıtlar:


80

Akıllılık yerine daima açıklığı tercih edin. Geçmiş yıllardaki en iyi programcı, kodunu hiç kimse anlayamadığı programdı. “Kodunu anlamıyorum, bir dahi olmalı” dediler. Günümüzde en iyi programcı, kimin kodunu anlayabildiğidir. Bilgisayar zamanı, programcının zamanından daha ucuz.

Herhangi bir aptal bir bilgisayarın anlayabileceği bir kod yazabilir. İyi programcılar, insanların anlayabileceği bir kod yazar. (M. Fowler)

Yani, hiç şüphesiz, A seçeneğine giderdim. Bu benim kesin cevabım.


8
Çok özlü ideolojisi, ama şartlar atama ile yanlış bir şey olmadığı gerçeği kalır. Döngüden önce veya bir döngüden önce veya bir döngüden çoğaltma kodundan çıkmak çok tercih edilir.
Miles Rout

26
@ MilesRout Var. Herhangi bir kodun beklemeyeceğiniz yan etkiye sahip olduğu, yani işlev argümanlarını ileten veya koşulluları değerlendiren bir sorun var. if (a=b)Kolayca yanılabileceğinden bahsetmiyorum bile if (a==b).
Arthur Havlicek

12
@Luke: "IDE'm X'i temizleyebilir, bu yüzden sorun değil" oldukça ikna edici. Sorun değilse, IDE neden "düzeltmeyi" kolaylaştırıyor?
Kevin,

6
@ArthurHavlicek Genel amacınıza katılıyorum, ancak koşullarda yan etkileri olan kod çok nadir değil: while ((c = fgetc(file)) != EOF)aklıma ilk gelen olarak.
Daniel Jour

3
+1 "Hata ayıklamanın, ilk başta bir program yazmaktan iki kat daha zor olduğunu düşünün, eğer yazarken olabildiğince zekiyseniz, nasıl hata ayıklayacaksınız?" BWKernighan
Christophe

32

Tulains Córdova'nın cevabında olduğu gibi altın kural, anlaşılır bir kod yazdığından emin olmaktır. Ancak bu sonuca katılmıyorum. Bu altın kural, kodunuzu koruyacak olan tipik programcının anlayabileceği kod yazmak anlamına gelir. Ve siz , kodunuzu korumanıza neden olacak tipik programcının kim olduğu konusunda en iyi yargıçsınız.

C ile başlamayan programcılar için, ilk versiyonun zaten bildiğiniz nedenlerden dolayı anlaşılması daha kolaydır.

Bu C stiliyle büyüyenler için, ikinci versiyonun anlaşılması daha kolay olabilir: onlara göre, kodun ne yaptığı, kendilerine, neden olduğu gibi yazıldığından daha az soru sorar ve onlara daha az soru sorar. , daha az dikey alan, ekranda daha fazla içeriğin görüntülenebileceği anlamına gelir.

Kendi mantığınıza güvenmek zorunda kalacaksınız. Kodunuzu anlaşılması en kolay olan kitleye ne yapmak istiyorsunuz? Bu kod bir şirket için yazılmış mı? O zaman hedef kitle muhtemelen o şirketteki diğer programcılar. Bu, kendinizden başka kimsenin üzerinde çalışmayacağı kişisel bir hobi projesi mi? O zaman kendi hedef kitleniz sensin. Bu kod başkalarıyla paylaşmak mı istiyorsun? O zaman bu diğerleri sizin hedef kitleniz. Bu kitleyle eşleşen sürümü seçin. Ne yazık ki, teşvik etmek için tercih edilen tek bir yol yoktur.


14

EDIT: Satır s[i] = '\0';ilk sürüme eklendi, böylece aşağıdaki 1. varyantta açıklandığı şekilde düzeltildi, bu artık soru kodunun şu andaki sürümü için geçerli değil.

İkinci versiyon, doğru olma özelliğine sahip, birincisi ise - hedef dizgiyi doğru şekilde sonlandırmıyor.

"Durumda atama", " boş karakteri kontrol etmeden önce her karakteri kopyala" kavramını çok öz bir şekilde ve derleyici için optimizasyonu biraz daha kolaylaştıracak şekilde ifade etmeyi sağlar, ancak bugünlerde birçok yazılım mühendisi bu kod stilini daha az okunabilir bulmaktadır . İlk sürümü kullanmakta ısrar ederseniz, ikisinden birini kullanmak zorunda kalırsınız.

  1. İkinci döngünün bitiminden sonra boş sonlandırmayı ekleyin (daha fazla kod ekleyerek, ancak okunabilirliğin değerli olduğunu söyleyebilirsiniz) veya
  2. döngü gövdesini "önce ata, sonra atanan karakteri kontrol et veya kaydet, sonra indeksleri artır" olarak değiştir. Döngünün ortasındaki durumun kontrol edilmesi, döngünün kırılması anlamına gelir (çoğu temizleyici tarafından kaşlarını çattıran netliği azaltır). Atanan karakterin kaydedilmesi geçici bir değişken eklemek anlamına gelir (netliği ve verimliliği azaltır). Her ikisi de bence avantajı ortadan kaldıracaktı.

Doğru, okunaklı ve özlü olmaktan iyidir.
user949300,

5

Tulains Córdova ve hvd tarafından verilen cevaplar netlik / okunabilirlik özelliklerini oldukça iyi örtmektedir. Koşulları atamanın lehine başka bir sebep olarak kapsama atmama izin verin . Durumda bildirilen bir değişken yalnızca bu ifadenin kapsamı içinde bulunur. Daha sonra bu değişkeni kazara kullanamazsınız. İçin döngü yaş için bu yapıyor. Ve yaklaşan C ++ 17'nin if ve switch için benzer bir sözdizimi getirmesi yeterince önemlidir :

if (int foo = bar(); foo > 42) {
    do_stuff();
}

foo = 23;   // compiler error: foo is not in scope

3

Hayır. Çok standart ve normal C tarzı. Örneğiniz kötü bir örnek, çünkü sadece bir for döngüsü olmalı, fakat genel olarak yanlış olan hiçbir şey yok.

if ((a = f()) != NULL)
    ...

örneğin (veya ile).


7
Bunda yanlış bir şeyler var; '! = NULL' ve bir C koşulundaki aklı daha doğaldır, sadece doğru ya da yanlış bir değer kavramıyla rahat olmayan geliştiricileri yerleştirmek için vardır.
Jonathan Cast,

1
@jcast Hayır, dahil edilmesi daha açık != NULL.
Miles Rout

1
Hayır, söylemesi daha açık (x != NULL) != 0. Sonuçta, C gerçekten kontrol ettiği şey bu, değil mi?
Jonathan Cast,

@jcast Hayır, değil. Bir şeyin yanlış ile eşit olup olmadığını kontrol etmek, herhangi bir dilde koşullandırmayı nasıl yazdığınız değildir.
Miles Rout

"Bir şeyin yanlış ile eşit olup olmadığına bakmak, koşullu dilleri herhangi bir dilde nasıl yazdığınız anlamına gelmez." Kesinlikle.
Jonathan Cast,

2

K & R günlerinde

  • 'C' taşınabilir montaj koduydu
  • Montaj kodunda düşünen programcılar tarafından kullanılmıştır
  • Derleyici çok fazla optimizasyon yapmadı
  • Çoğu bilgisayarda “karmaşık komut setleri” vardı, örneğin while ((s[i++]=t[j++]) != '\0')çoğu CPU'daki bir talimatla eşleşirdi (Dec VAC'yi bekliyoruz)

Orada günler

  • C kodunu okuyan çoğu kişi montaj kodu programcıları değildir
  • C derleyicileri çok fazla optimizasyon yapar, bu nedenle okunması kolay kod aynı makine koduna çevrilir.

(Her zaman parantez kullanmaya ilişkin bir not - ilk kod seti bazı “gereksiz” {}olduğundan dolayı daha fazla yer kaplar; deneyimlerime göre bunlar genellikle derleyiciden çok kötü şekilde birleştirilen kodların önlenmesini sağlar ve yanlış “;” yerleşimlerinin hatalı olmasına izin verir araçlar tarafından tespit edildi.)

Ancak eski günlerde kodun 2. sürümü okurdu. (Eğer doğru anladıysam!)

concat(char* s, char *t){      
    while (*s++);
    --s;
    while (*s++=*t++);
}

2

Bunu yapabilmek bile çok kötü bir fikir. Halk arasında "Dünyanın Son Böceği" olarak da bilinir:

if (alert = CODE_RED)
{
   launch_nukes();
}

Eğer büyük olasılıkla bir hata yapmak değil iken oldukça şiddetli yanlışlıkla berbat ve kod temeli bir zor find hataya neden gerekirse, çok kolaydır. Çoğu modern derleyici, bir koşullu içerideki atamalar için uyarı verir. Bir sebepten dolayı oradalar ve onları gözetlemede iyi iş çıkardın ve bu yapıdan kaçının.


Bu uyarıdan önce, CODE_RED = alertderleyici hatası vermesi için yazacağız .
Ian

4
@ Ian Yoda Koşullu denir. Onlar okumak zor. Talihsizlik onlar için gerekliliğidir.
Mason Wheeler

Çok kısa bir tanıtım "alışmak" döneminden sonra, Yoda koşullarını okumak normal şartlardan daha zor değildir. Bazen daha okunaklıdırlar . Örneğin, ifs / elseif diziniz varsa, daha yüksek vurgu için solda test edilen durumun olması hafif bir iyileştirme IMO'dur.
user949300

2
@ user949300 İki kelime: Stockholm Sendromu: P
Mason Wheeler

2

Her iki stil de iyi biçimlendirilmiş, doğru ve uygun. Hangisinin daha uygun olduğu, büyük ölçüde şirketinizin stil kurallarına bağlıdır. Modern IDE'ler, başka türlü karışıklık kaynağı olabilecek alanları açıkça vurgulayan canlı sözdizimi dizisi kullanımı yoluyla her iki stilin kullanımını da kolaylaştıracaktır.

Örneğin, aşağıdaki ifade NetBeans tarafından vurgulanmıştır :

if($a = someFunction())

"yanlışlıkla atama" gerekçesiyle.

görüntü tanımını buraya girin

Netbeans’a açıkça söylemek gerekirse, “evet, gerçekten yapmalıydım…” ifadesi bir parantez içine alınabilir.

if(($a = someFunction()))

görüntü tanımını buraya girin

Günün sonunda, tüm geliştirme süreci kolaylaştırmak için şirket tarzı yönergeleri ve modern araçların mevcudiyeti aşağı kaynar.

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.