Bileşik AND / VEYA


13

Maksimum okunabilirlik için karmaşık bileşik VE / VEYA ifadelerini nasıl şekillendirirsiniz? Girintiyi nasıl girersiniz ve satır sonlarını nereye koyarsınız? Benim özel durumum şuna benzer. Her şeyi tek bir satıra parçalamaktan kesinlikle daha iyidir, ancak yine de dağınık görünüyor.

if (
    (
        x == y
        && a != b
        && p.isGood() 
        && (
            i + u == b
            || q >= a
        )
    )
    || k.isSomething()
    || m > n
) {
    doSomething();
}

1
Aptal girintileme ve parantez / küme yapısı kasıtlı mı yoksa stilin bir parçası mı?
Ed

Komik. Aynı soruyu bir hafta önce SO'ya sordum ve kapatıldı. Bu soruyu bir yerde canlı gördüğüme sevindim!
Eric Belair

Yanıtlar:


6

Her küçük adım için boole değişkenleri oluşturun:

bool step1 = i + u == b || q >= a;
bool step2 = a != b && p.isGood() && group1;
bool step3 = group2 || k.isSomething() || m > n;
if (step3) { doSomething(); }

Bu elbette her adım için farklı isimler dışında Lacrymology'nin cevabına benzer.

Eğer isim ise step1, step2ve step3iyi kavramsal mantıklı gelen şekilde, bu kadar çok okunaklı tarafından olmalıdır. p.isGood()ve k.isSomething()bazen orijinal kodunuzda bulunmayacağı durumlarda çağrılabilir, bu nedenle bu işlevler pahalıysa veya bu kodu çok sıkı bir döngüde çalıştırıyorsanız bu bir seçenek olmaz.

Öte yandan, yeni değişkenler yaratmanın başarabileceği performans isabeti hakkında endişelenmenize gerek yok; iyi bir derleyici onları optimize eder.

Dikdörtgen çarpışma algılamalı bir örnek (yukarıda belirtilen performans isabeti nedeniyle muhtemelen kullanmazsınız):

if((a.x + a.width >= b.x || b.x + b.width >= a.x)
 && (a.y + a.height >= b.y || b.y + b.width >= a.y)
)
{ collision(); }

Olabilir:

bool horizMatch = a.x + a.width >= b.x || b.x + b.width >= a.x;
bool vertMatch = a.y + a.height >= b.y || b.y + b.width >= a.y;
if(horizMatch && vertMatch) { collision(); }

Ayrıca, kodunuzu olduğu gibi bırakmak istiyorsanız, bunun da tamamen iyi olacağını düşünüyorum. Dürüst olmak gerekirse kodunuzun oldukça okunaklı olduğunu düşünüyorum. Açıkçası tam a b x y i u p k m nolarak ne olduğunu bilmiyorum , ama yapıya gelince, bana iyi geliyor.


8

Şartlarım bu kadar karmaşık hale gelirse, kodumu genellikle daha modüler olacak şekilde yeniden çarpanlarına ayırırım.


Bu durumda refrakter olmaktan kaçınırdım. Bu küçük işlevleri ayrı ayrı test etmek saçma olurdu ve tanımların işlevin dışında sarkması kodu daha az belirgin hale getirir.
Rei Miyasaka

Ne kadar iyi refactor bağlıdır. Kodunuzda bir cebir ders kitabı gibi görünüyordu koşullu bir dize yerine anlamlı işlev isimleri sahip olmak için kodunuzu daha belirgin hale getirir iddia ediyorum.
JohnFx

Görsel olarak, işlevleriniz dışarıda sarkarsınız ve işlevin tam olarak ne yaptığını hemen belli olmaz. Yukarı kaydırıncaya kadar anlık olarak bir kara kutu. Fonksiyonlarda işlevlere izin veren bir dilde değilseniz, ne kadar iyi refraktör olursanız olun, ne okuyucu için ne de yazar için çok uygun olacağını düşünmüyorum. Ve işlevlerde işlevlere izin veren bir dildeyseniz, sözdiziminin bir bağlanma veya değişken bildirmekten neredeyse hiç farklı olmadığı, örneğin let x = a > bveya let f a b = a > b.
Rei Miyasaka

Değişkenler eşit derecede iyi çalışır. Ben de yeniden düzenlemeyi düşünürdüm.
JohnFx

Ah tamam.
Rei Miyasaka

8

Bu karmaşıklık düzeyinde daha çok böyle bir şey yapardım

bool doIt = x == y && a != b && p.isGood();
doIt &= ( i + u == b || q >= a);
doIt |= k.isSomething() || m > n;

if(doIt)
{
    doSomething();
}

bu çirkin, ama okunabilir ve derleyici onu nasıl yeniden düzenleyeceğini bileceğinden eminim.

Öte yandan, kendimi böyle bir IF ifadesi yazarken görürsem, çözümü yeniden düşünürüm, çünkü CERTAIN'ı daha basit yapmanın veya en azından bu durumun bazılarını soyutlamanın bir yolu var (örneğin: belki x == y && a != b && p.isGood()gerçekten sadece demek this->isPolygon()ve bu yöntemi yapabilirim;


4

Zaman içinde dikey hizalamaya daha az takıntılı oluyorum, ancak çok satırlı ifadelere sahip genel formum ...

if (   (   (expr1 == expr2)
        || (expr3 == expr4)
        || (expr5 == expr6)
       )
    && (   (expr7 == expr8)
        || (expr9 == expra)
       )
   )
{
  blah;
}

Anahtar noktaları...

  • Yakın parensler, parantezlerde olduğu gibi açık parenslerle dikey olarak hizalanır.
  • Bir çizgiye uyan alt ifadeler bir çizginin içindedir ve solda dikey olarak hizalanır. Okunabilirliğe yardımcı olduğunda, bu tek satırlı parçalar içindeki infix operatörleri de dikey olarak hizalanır.
  • Kapanış parantezleri doğal olarak neredeyse boş çizgiler oluşturur ve şeyleri görsel olarak gruplandırmaya yardımcı olur.

Bazen, biçimlendiririz +ve *veya başka operatörler de böyle. Oldukça karmaşık birkaç ifade, bir toplam ürün veya toplam ürün biçimini alır (boole "toplamları" ve "ürünleri" anlamına gelebilir), bu nedenle tutarlı bir stilin değerli olması için muhtemelen yeterince yaygındır.

Buna rağmen dikkatli olun. Aşırı karmaşık ifadeyi daha okunabilir hale getirmek için girintiyi kullanmak yerine yeniden ifade etmek (ifadenin parçalarını bir işleve taşımak veya ara parçaları bir değişkente hesaplamak ve saklamak) daha iyidir.

Yakın taraflarınızı sağ tarafta istiflemeyi tercih ediyorsanız, nefret etmiyorum, ama sanırım çok kötü değil. Çok ileri gittiğinde, bir hatanın parantezlerin ne yaptığını yanlış girerek girintiyi bırakma riskini taşıyorsunuz.

if (   (   (expr1 == expr2)
        || (expr3 == expr4)
        || (expr5 == expr6))

    && (   (expr7 == expr8)
        || (expr9 == expra)))
{
  blah;
}

+1 Tarzını beğendim. Sorumu doğrudan yanıtladı, ama bence Rei Miyasaka sorunun kökünü çiviledi. Rei'nin yöntemini yapmak için tembel olursam, stilinizi kullanacağım.
JoJo

Vay canına, bu gerçekten çok güzel.
Rei Miyasaka

1

http://www.codinghorror.com/blog/2006/01/flattening-arrow-code.html

JohnFx'in yanı sıra Lacrymology'nin cevabına katılıyorum. Küçük hedeflere ulaşan bir grup fonksiyon (tercihen statik) inşa eder ve daha sonra bunları akıllı bir şekilde geliştiririm.

Peki böyle bir şeye ne dersin? Bu mükemmel bir çözüm değildir, ancak işe yarar. Bunu daha fazla temizlemenin yolları vardır, ancak daha spesifik bilgilere ihtiyaç vardır. Not: derleyici akıllı olduğu için bu kod hızlı çalışmalıdır.

// Currently based on members or global vars
// (which is often a bad idea too)
function doSomethingCondirionally()
{
  if (k.isSomething() || m > n)
  {
    doSomething();
    return;
  }

  // Else ... 
  if (x != y) return;
  if (a == b) return;
  if (!p.isGood()) return;

  // Final, positive check
  if (i + u == b || q >= a)
  {
    doSomething();
  }
}

Bir işlevden yalnızca bir çıkış noktasına sahip olmak değer verdiğiniz bir şeyse (örneğin, işlevsel bir dildeyseniz), bu en iyi seçenek, hatta kullanılabilir bir seçenek olmayabilir. Yani, evet, sık sık yaptığım bu.
Rei Miyasaka

Ya Javascript gibi yorumlanmış bir dilse?
JoJo

@ Rei Miyasaka, ne kadar değer verdiğimi bir dile bağlıyorum. LISP dil ailesini sevsem de, birisini işyerinde kullanmak zorunda kalmadım. Ben başka birinin işlevini yeniden faktör gerekiyor ama başka bir kod (genellikle bir gerçeklik) dokunmak zorunda kalırsanız, o zaman yukarıdaki gibi bir şey yapmak istiyorum. Bu mantığı sıfırdan yazabilir / yeniden yazabilirsem, yaklaşımım farklı olacaktır, ancak yazarın burada ne yapmaya çalıştığının belirli bir örneği olmadan bu kodu yazamam.
İş

1
@Rei Miyasaka, O kişi bir dahi olabilir ya da bok dolu olabilir. Her şeyi bilmiyorum, ama o kişinin tek çıkış noktasına karşı savunmasını bilmek isterdim. Burada ve SO ile ilgili bazı tartışmalar var ve aldığım izlenim, bu yaklaşımın 80'lerde akademisyenler arasında popüler olduğu, ancak artık önemli olmadığı ve aslında okunabilirliği engelleyebileceği yönündeydi. Tabii ki, her şeyi LINQ fonksiyonel tarzda yapıyorsanız, bu sorun bile ortaya çıkmaz.
İş

2
@Job @Steve Bence bu açık anlaşma gerektiren dillerde daha önemli bir kılavuzdur. Açıkçası her işlev için değil, ama yeni başlayan programcıların serbest kaynakları unutmayı önlemek için tutmaları teşvik edilen bir alışkanlıktır.
Rei Miyasaka

1

Değeri için, örneğinizin yazdığım karmaşık tahminlere çok benzediğini görünce şaşırdım. Diğerleriyle, karmaşık bir yüklemin sürdürülebilirlik veya okunabilirlik için en büyük şey olmadığına katılıyorum, ancak bazen ortaya çıkıyorlar.

Bu parçayı doğru yaptığınızı vurgulayayım: && a != b ASLA mantıksal bağlayıcıyı bir satırın sonuna koymayın, görsel olarak kaçırmak çok kolay. Hattın sonuna ASLA operatör koymamanız gereken başka bir yer de, böyle bir operatörle birlikte dillerde dize birleştirme işlemidir.

Bunu yap:

String a = b
   + "something"
   + c
   ;

Bunu yapma:

String a = b +
   "something" +
   c;

Mantıksal bağlayıcılar için iddianızı destekleyen herhangi bir mantığınız veya çalışmanız var mı, yoksa sadece tercihiniz gerçek olarak mı belirtiliyor? Bunu çeşitli yerlerde çok duydum ve asla anlamadım (geçerli bir [yanlış yönlendirilmişse) yoda koşullarının aksine).
Caleb Huitt - cjhuitt

@Caleb - Matematik yüzyıllardır bu tarzda dizgindir. Kodu gözden geçirirken, her satırın sol tarafına odaklanıyoruz. Bir işleçle başlayan satırlar açık bir şekilde önceki satırın devamıdır ve yanlış girintili yeni bir ifade değildir.
kevin cline

Matematiksel operatörlerin önekini de severim. Programlama kariyerime geç geldiğimi fark ettim :)
JoJo

0

Koşullu bu kadar karmaşıksa, genellikle parçalara bölünmesi gerektiğinin bir göstergesidir. Bir ara değişkene belki bir cümle atanabilir. Belki bir fıkra yardımcı bir yönteme dönüştürülebilir. Genelde bir satırda çok fazla and ve ors olmamasını tercih ederim.


0

Kodu birden çok ifadeye ayırarak anlaşılmasını kolaylaştırırsınız. Ama gerçek bir ninja böyle bir şey yapardı. :-)

if
(
    (
        x == y
    &&
        a != b
    &&
        p.isGood()
    &&
        (
            i + u == b
        ||
            q >= a
        )
    )
||
    k.isSomething()
||
    m > n
)
{
    doSomething();
}

5
Ben bir boşluk hayranıyım, ama bu benim zevkime göre neredeyse boş çizgilerle aşırı doldurulmuş.
Steve314
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.