จัดแต่งทรงผมผสมและ / หรือถ้าคำสั่ง


13

คุณจะคอมไพล์คอมเพล็กซ์ซับซ้อนอย่างไรและ / หรือถ้าคำสั่งสำหรับการอ่านสูงสุด คุณเยื้องอย่างไรและคุณวางเส้นแบ่งที่ใด สถานการณ์เฉพาะของฉันคือบางสิ่งดังต่อไปนี้ มันดีกว่าการทุบทุกอย่างให้เป็นเส้นเดียว แต่ก็ดูยุ่งเหยิง

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

1
เยื้องเยื้องและวงเล็บ / โครงสร้างรั้งตั้งใจหรือเป็นส่วนหนึ่งของสไตล์หรือไม่
Ed S.

ตลก. ฉันถามคำถามเดียวกันนี้เมื่อสัปดาห์ที่แล้วและมันก็ปิด ดีใจที่เห็นคำถามนี้มีชีวิตอยู่ที่ไหนสักแห่ง!
Eric Belair

คำตอบ:


6

สร้างตัวแปรบูลีนสำหรับแต่ละขั้นตอนเล็ก ๆ :

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

หลักสูตรนี้คล้ายกับคำตอบของ Lacrymology ยกเว้นมีชื่อแตกต่างกันในแต่ละขั้นตอน

หากคุณตั้งชื่อstep1, step2และstep3ในรูปแบบที่ให้ความรู้สึกความคิดที่ดีนี้ควรจะเป็นโดยไกลที่ชัดเจนมากที่สุด p.isGood()และk.isSomething()บางครั้งอาจถูกเรียกใช้ในสถานการณ์ที่มันไม่ได้อยู่ในรหัสดั้งเดิมของคุณดังนั้นนี่จะไม่เป็นตัวเลือกหากฟังก์ชั่นเหล่านั้นมีราคาแพงหรือหากคุณกำลังเรียกใช้รหัสนี้ในวงที่แน่นมาก

ในอีกทางหนึ่งคุณไม่จำเป็นต้องกังวลเกี่ยวกับประสิทธิภาพที่สร้างตัวแปรใหม่ที่อาจเกิดขึ้น คอมไพเลอร์ที่ดีจะปรับมันให้เหมาะสม

ตัวอย่างที่มีการตรวจจับการชนกันของรูปสี่เหลี่ยมผืนผ้า (ซึ่งคุณอาจไม่ได้ใช้เนื่องจากการทำงานที่มีประสิทธิภาพดังกล่าว):

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(); }

อาจกลายเป็น:

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(); }

นอกจากนี้หากคุณต้องการปล่อยให้รหัสของคุณเป็นเช่นนั้นฉันคิดว่ามันก็ดีเหมือนกัน ฉันคิดว่ารหัสของคุณค่อนข้างชัดเจน เห็นได้ชัดว่าฉันไม่ทราบว่าสิ่งที่แน่นอนa b x y i u p k m nแต่เท่าที่โครงสร้างไปมันดูดีสำหรับฉัน


8

ฉันมักจะให้รหัสของฉันเป็นแบบแยกส่วนอีกครั้งหากเงื่อนไขของฉันมีความซับซ้อน


ฉันจะหลีกเลี่ยงการหักเหของแสงในสถานการณ์นี้ มันคงโง่มากที่จะทดสอบฟังก์ชั่นเล็ก ๆ อย่างแยกและการมีคำจำกัดความที่ห้อยอยู่ภายนอกฟังก์ชั่นทำให้โค้ดนั้นไม่ค่อยปรากฏตัว
Rei Miyasaka

ขึ้นอยู่กับว่าคุณ refactor ดีแค่ไหน ฉันยืนยันว่ามันทำให้โค้ดของคุณมีความชัดเจนมากขึ้นในการมีชื่อฟังก์ชั่นที่มีความหมายแทนที่จะเป็นสตริงของเงื่อนไขที่ดูเหมือนว่าตำราพีชคณิตที่ขว้างในโค้ดของคุณ
JohnFx

แม้ว่าคุณจะมีฟังก์ชั่นของคุณห้อยอยู่ข้างนอกและมันจะไม่ชัดเจนทันทีว่าฟังก์ชั่นทำอะไร มันเป็นกล่องสีดำชั่วขณะจนกว่าคุณจะเลื่อนขึ้น ถ้าคุณไม่ได้ใช้ภาษาที่ให้ฟังก์ชั่นการใช้งานฉันไม่คิดว่ามันจะสะดวกสำหรับผู้อ่านหรือนักเขียนไม่ว่าคุณจะหักเหดีแค่ไหนก็ตาม และถ้าคุณในภาษาที่ช่วยให้การทำงานในฟังก์ชั่นแล้วโอกาสที่ไวยากรณ์คือแทบจะไม่แตกต่างจากการประกาศหรือตัวแปรที่มีผลผูกพันแทนเช่นหรือlet x = a > b let f a b = a > b
Rei Miyasaka

ตัวแปรจะทำงานได้ดีอย่างเท่าเทียมกัน ฉันจะพิจารณาการปรับโครงสร้างด้วยเช่นกัน
JohnFx

อ่าโอเค.
Rei Miyasaka

8

ฉันจะทำอะไรมากกว่านี้ในระดับความซับซ้อนนี้

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

if(doIt)
{
    doSomething();
}

มันน่าเกลียด แต่มันสามารถอ่านได้และฉันค่อนข้างมั่นใจว่าคอมไพเลอร์จะรู้วิธีการปรับโครงสร้าง

ในทางกลับกันถ้าฉันเคยเห็นตัวเองในสถานการณ์ของการเขียนคำสั่ง IF เช่นนั้นฉันคิดใหม่วิธีแก้ปัญหาเพราะฉัน CERTAIN มีวิธีทำให้ง่ายขึ้นหรืออย่างน้อยก็สรุปบางเงื่อนไข (เช่น: อาจจะx == y && a != b && p.isGood()แค่หมายความว่าจริง ๆthis->isPolygon()และฉันก็สามารถทำวิธีนั้นได้


4

ฉันหมกมุ่นอยู่กับการจัดแนวตั้งน้อยลงเมื่อเวลาผ่านไป แต่รูปแบบทั่วไปของฉันที่มีการแสดงออกหลายบรรทัดคือ ...

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

ประเด็นสำคัญ ...

  • parens ที่ใกล้ชิดจะจัดเรียงในแนวตั้งกับ parens ที่เปิดอยู่เหมือนกับวงเล็บปีกกา
  • นิพจน์ย่อยที่พอดีภายในหนึ่งบรรทัดอยู่ภายในหนึ่งบรรทัดและจัดเรียงในแนวตั้งทางด้านซ้าย ในกรณีที่ช่วยให้สามารถอ่านได้ตัวดำเนินการมัดภายในชิ้นส่วนบรรทัดเดียวเหล่านั้นจะถูกจัดเรียงในแนวตั้งด้วย
  • เครื่องมือจัดฟันแบบปิดจะสร้างเส้นที่เกือบจะว่างเปล่าโดยธรรมชาติช่วยในการจัดกลุ่มสิ่งของ

บางครั้งฉันจะจัดรูปแบบ+และ*หรือผู้ให้บริการรายอื่นเช่นนี้ การแสดงออกที่ซับซ้อนบางอย่างใช้รูปแบบผลรวมของผลิตภัณฑ์หรือผลรวมของผลิตภัณฑ์ (ซึ่งสามารถอ้างถึง "ผลรวม" แบบบูลและ "ผลิตภัณฑ์") ดังนั้นจึงอาจเป็นเรื่องธรรมดาพอที่สไตล์ที่สอดคล้องกันจะคุ้มค่า

ระวังด้วยนะ มันมักจะดีกว่าที่จะ refactor (ย้ายส่วนต่าง ๆ ของนิพจน์ลงในฟังก์ชั่นหรือคำนวณและเก็บชิ้นส่วนกลางในตัวแปร) แทนที่จะใช้การเยื้องเพื่อพยายามทำให้นิพจน์ overcomplex อ่านได้ง่ายขึ้น

หากคุณต้องการวางซ้อนโคลสอัพทางด้านขวามือฉันไม่ได้เกลียดแต่ฉันคิดว่ามันไม่ได้แย่เกินไป เมื่อนำมาไกลเกินไปคุณจะเสี่ยงต่อความผิดพลาดที่อาจทำให้การเยื้องแสดงความผิดพลาดในสิ่งที่วงเล็บทำ

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

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

+1 ฉันชอบสไตล์ของคุณ มันตอบคำถามของฉันโดยตรง แต่ฉันคิดว่า Rei Miyasaka จับต้นตอของปัญหา ถ้าฉันขี้เกียจทำวิธีของ Rei ฉันจะใช้สไตล์ของคุณ
JoJo

ว้าวนี่มันดีจริงๆ
Rei Miyasaka

1

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

ฉันเห็นด้วยกับคำตอบของ JohnFx เช่นเดียวกับ Lacrymology ฉันจะสร้างกลุ่มของฟังก์ชั่น (คงที่โดยเฉพาะอย่างยิ่ง) ที่บรรลุเป้าหมายเล็ก ๆ แล้วสร้างพวกเขาในทางที่ชาญฉลาด

งั้นเรื่องแบบนี้ล่ะ? หมายเหตุนี่ไม่ใช่โซลูชันที่สมบูรณ์แบบ แต่ใช้งานได้ มีวิธีการล้างข้อมูลนี้ต่อไป แต่ต้องการข้อมูลที่เฉพาะเจาะจงมากขึ้น หมายเหตุ: รหัสนี้ควรจะทำงานได้อย่างรวดเร็วสำหรับคอมไพเลอร์ที่ฉลาด

// 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();
  }
}

หากมีเพียงหนึ่งจุดออกจากฟังก์ชั่นเป็นสิ่งที่คุณให้คุณค่า (เช่นถ้าคุณอยู่ในภาษาที่ใช้งานได้) นี่อาจไม่ใช่ตัวเลือกที่ดีที่สุดหรือแม้กระทั่งจุดที่มีอยู่ ที่กล่าวว่าใช่นี่คือสิ่งที่ฉันมักจะทำ
Rei Miyasaka

เกิดอะไรขึ้นถ้ามันเป็นภาษาที่ตีความเช่น Javascript
JoJo

@ Rei Miyasaka ฉันให้ความสำคัญกับภาษามากน้อยเพียงใด ในขณะที่ฉันชอบภาษาตระกูล LISP ฉันไม่ต้องใช้ภาษาในที่ทำงาน หากฉันต้องคำนึงถึงการทำงานของคนอื่น แต่ไม่ต้องสัมผัสโค้ดอื่น (มักจะเป็นความจริง) จากนั้นฉันก็จะทำสิ่งต่าง ๆ ข้างต้น หากฉันสามารถเขียน / เขียนตรรกะนี้ใหม่ได้ดังนั้นวิธีการของฉันจะแตกต่างกัน แต่ฉันไม่สามารถเขียนรหัสดังกล่าวได้โดยไม่ต้องมีตัวอย่างเฉพาะของสิ่งที่ผู้เขียนพยายามทำที่นี่
งาน

1
@Rei Miyasaka บุคคลนั้นอาจเป็นอัจฉริยะหรือเต็มไปด้วยอึ ฉันไม่รู้ทุกอย่าง แต่ฉันอยากรู้ว่าการป้องกันจุดทางออกเดียวของบุคคลนั้น มีการถกเถียงกันเรื่องนี้และที่อื่น ๆ และความประทับใจที่ฉันได้รับก็คือวิธีการนี้ได้รับความนิยมในหมู่นักวิชาการในยุค 80 บางที แต่ก็ไม่สำคัญอีกต่อไปและในความเป็นจริงอาจขัดขวางการอ่านได้ แน่นอนถ้าคุณทำทุกอย่างในรูปแบบการทำงานของ LINQ แล้วปัญหานี้ก็จะไม่เกิดขึ้น
งาน

2
@Job @Steve ฉันคิดว่านี่เป็นแนวทางที่สำคัญกว่าในภาษาที่ต้องมีการจัดสรรคืนอย่างชัดเจน เห็นได้ชัดว่าไม่ใช่สำหรับทุกฟังก์ชั่น แต่อาจเป็นนิสัยที่โปรแกรมเมอร์มือใหม่ได้รับการสนับสนุนให้เก็บไว้เพื่อหลีกเลี่ยงการลืมทรัพยากรฟรี
Rei Miyasaka

1

สำหรับสิ่งที่คุ้มค่าฉันรู้สึกประหลาดใจเมื่อเห็นตัวอย่างของคุณดูเหมือนภาคแสดงที่ซับซ้อนที่ฉันเขียน ฉันเห็นด้วยกับคนอื่น ๆ ว่าคำกริยาที่ซับซ้อนไม่ใช่สิ่งที่ดีที่สุดสำหรับการบำรุงรักษาหรือการอ่าน แต่บางครั้งพวกเขาก็เกิดขึ้น

ผมขอย้ำว่าคุณทำส่วนนี้ถูกต้อง: อย่า&& a != b ใส่ตัวเชื่อมต่อแบบลอจิคัลที่ส่วนท้ายของบรรทัดมันง่ายเกินไปที่จะพลาดการมองเห็น สถานที่ที่คุณไม่ควรวางโอเปอเรเตอร์ที่ส่วนท้ายของบรรทัดนั้นคือการต่อสตริงเข้าด้วยกันในภาษาที่มีโอเปอเรเตอร์ดังกล่าว

ทำเช่นนี้:

String a = b
   + "something"
   + c
   ;

อย่าทำสิ่งนี้:

String a = b +
   "something" +
   c;

คุณมีเหตุผลหรือการศึกษาที่สนับสนุนการยืนยันตัวเชื่อมต่อแบบลอจิคัลหรือว่าเป็นเพียงการตั้งค่าของคุณตามความเป็นจริง? ฉันเคยได้ยินเรื่องนี้มากมายในหลาย ๆ สถานที่และไม่เคยเข้าใจเลย (ไม่เหมือนเงื่อนไขโยดาซึ่งมีเหตุผลที่ถูกต้อง (ถ้าเข้าใจผิด))
Caleb Huitt - cjhuitt

@Caleb - คณิตศาสตร์ได้รับเรียงพิมพ์ในแบบนั้นมานานหลายศตวรรษ เมื่ออ่านรหัสเราจะเน้นที่ด้านซ้ายของแต่ละบรรทัด บรรทัดที่ขึ้นต้นด้วยโอเปอเรเตอร์คือความต่อเนื่องของบรรทัดก่อนหน้าและไม่ใช่ข้อความใหม่ที่เยื้องอย่างไม่ถูกต้อง
วินไคลน์

ฉันชอบคำนำหน้าของตัวดำเนินการทางคณิตศาสตร์ด้วย ฉันรู้ว่ามันจะสายในอาชีพการเขียนโปรแกรมของฉัน :)
JoJo

0

หากเงื่อนไขมีความซับซ้อนนั้นมักจะเป็นข้อบ่งชี้ว่าควรแยกออกเป็นส่วน ๆ บางทีหนึ่งประโยคสามารถกำหนดให้กับตัวแปรกลาง บางทีประโยคหนึ่งสามารถเปลี่ยนเป็นวิธีช่วยได้ โดยทั่วไปฉันไม่ต้องการมีหลาย ands และ ors ในหนึ่งบรรทัด


0

คุณสามารถแบ่งรหัสออกเป็นหลาย ๆ คำสั่งทำให้เข้าใจได้ง่าย แต่นินจาตัวจริงจะทำอะไรแบบนี้ :-)

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

5
ฉันเป็นแฟนตัวยงของสเปซ แต่นี่มันเกินความคาดหมายไปแล้วสำหรับฉัน
Steve314
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.