ฉันควรใช้คำสั่งสลับหรือยาวถ้า…โซ่อื่น ๆ ?


36

บ่อยครั้งที่ฉันได้ยินเกี่ยวกับคำสั่ง switch มันวางตัวเป็นวิธีการแทนที่ยาวถ้า ... else chains แต่ดูเหมือนว่าเมื่อฉันใช้คำสั่งเปลี่ยนฉันกำลังเขียนรหัสเพิ่มเติมที่ฉันจะเขียนถ้า ... อื่น นอกจากนี้คุณยังมีปัญหาอื่น ๆ เช่นการรักษาตัวแปรทั้งหมดสำหรับการโทรทั้งหมดอยู่ในขอบเขตเดียวกัน

นี่คือโค้ดบางส่วนที่แทนโฟลว์ที่ฉันเขียนตามปกติ ( ขอบคุณ diam )

String comment;   // The generated insult.
int which = (int)(Math.random() * 3);  //  Result is 0, 1, or 2.

if (which == 0) {
    comment = "You look so much better than usual.";
} else if (which == 1) {
    comment = "Your work is up to its usual standards.";
} else if (which == 2) {
    comment = "You're quite competent for so little experience.";
} else {
    comment = "Oops -- something is wrong with this code.";
}

จากนั้นพวกเขาต้องการให้ฉันแทนที่ด้วยสิ่งนี้:

String comment;   // The generated insult.
int which = (int)(Math.random() * 3);  //  Result is 0, 1, or 2.

switch (which) {
    case 0:  
             comment = "You look so much better than usual.";
    break;
    case 1:  
             comment = "Your work is up to its usual standards.";
    break;
    case 2:  
             comment = "You're quite competent for so little experience.";
    break;
    default: 
             comment = "Oops -- something is wrong with this code.";
}

ดูเหมือนว่ารหัสมากขึ้นในไวยากรณ์ที่น่าอึดอัดใจมากขึ้น แต่มีข้อดีกว่าการใช้คำสั่ง switch หรือไม่?


ฮึ. ใช่นั่นเป็นเรื่องที่ใหญ่กว่า แต่เฉพาะในตระกูล C เพราะไวยากรณ์ของคำสั่ง case เป็นเรื่องที่น่าเกลียดมาก
Mason Wheeler

5
การเรียงลำดับของสิ่งนี้ได้รับการกล่าวถึงมากใน stackoverflow: - stackoverflow.com/questions/449273/ … - stackoverflow.com/questions/767821/… - stackoverflow.com/questions/97987/switch-vs-if-else
Yevgeniy Brikman

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

สวิตช์เร็วขึ้นอย่างน้อยใน. net ฉันไม่รู้เกี่ยวกับ Java
Knerd

สามารถ refactored ในพจนานุกรมของคดีและวิธีการและหนึ่ง "ถ้า"
Pavel Yermalovich

คำตอบ:


57

สำหรับสถานการณ์นี้โดยเฉพาะอย่างยิ่งดูเหมือนว่าฉันว่าทั้งสองifและcaseเป็นทางเลือกที่น่าสงสาร ฉันจะใช้อาร์เรย์ง่าย ๆ :

String comments[] = {
    "You look so much better than usual.",
    "Your work is up to its usual standards.",
    "You're quite competent for so little experience."
};

String comment = comments[(int)(Math.random() * 3)];

3ตามบันทึกข้างคุณโดยทั่วไปควรคำนวณคูณขึ้นอยู่กับขนาดของอาร์เรย์มากกว่ายากเข้ารหัส

ในกรณีที่คุณจะใช้เคส / สวิตช์ความแตกต่างจากการเรียงซ้อนของifข้อความ (หรืออย่างน้อยหนึ่งความแตกต่างที่สำคัญ) คือการswitchปรับกึ่งอัตโนมัติตามจำนวนและความหนาแน่นของค่าโดยอัตโนมัติในขณะที่ข้อความเรียงซ้อนของifคอมไพเลอร์ มีทางเลือกน้อย แต่เพื่อสร้างโค้ดตามที่คุณเขียนให้ทดสอบค่าหนึ่งค่าหลังจากนั้นจนกว่าจะพบการจับคู่ มีเพียงสามกรณีจริง ๆ ที่แทบจะไม่กังวล แต่มีจำนวนเพียงพอที่จะสามารถ / อาจมีนัยสำคัญ


ตัวอย่างนั้นเป็นเพียงตัวอย่าง BTW ไม่ทราบว่าคอมไพเลอร์สามารถปรับให้เหมาะสมเช่นนั้น
TheLQ

6
@JBRWilkinson ในกรณีนี้ค่านอกขอบเขตสามารถทำได้ผ่านคอมไพเลอร์บั๊กซึ่งฉันไม่อยากใช้เวลามาก (ข้อผิดพลาดในโค้ดของฉันเพื่อทดสอบผลลัพธ์เป็นไปได้มากเท่ากับในโค้ดที่สร้าง) ในสถานการณ์ที่ค่านอกขอบเขตเป็นข้อกังวลที่แท้จริง (เช่นดัชนีที่ได้รับจากรหัสอื่น ๆ ) ฉันจะตรวจสอบขอบเขตก่อนและใช้เป็นดัชนีเฉพาะหลังจากการตรวจสอบ
Jerry Coffin

4
ผมคิดว่าคำตอบนี้เป็นที่เฉพาะเจาะจงมากเกี่ยวกับตัวอย่างเช่นในขณะที่คำถามคือทั่วไปมากขึ้นกว่าที่ ...
Khelben

3
@Kelben: ฟังดูเหมือนว่าคุณไม่ได้อ่านคำตอบทั้งหมด ย่อหน้าสุดท้ายเน้นปัญหาที่กว้างขึ้น ผมพบว่ามีปัญหา แต่เป็นจำนวนน้อยมากในสถานการณ์ที่ผมคิดว่าทั้งcaseคำสั่งหรือน้ำตกของifงบที่เหมาะสม ส่วนใหญ่แล้วพวกเขาจะเป็น (ปานกลาง) แทนประเภทของแผนที่ / อาเรย์บางอย่างและคุณก็ควรจะใช้อย่างใดอย่างหนึ่งโดยตรง
Jerry Coffin

1
@Titou: เว้นแต่ว่าคอมไพเลอร์เป็นสุดยอดอย่างเด็ดขาดอาร์เรย์จะถูกสร้างขึ้นหนึ่งครั้งในเวลารวบรวมและหลังจากนั้นคุณเพียงแค่ใช้โครงสร้างแบบคงที่ ตัวอย่างเช่นหากคุณทำเช่นนี้ใน C หรือ C ++ คุณต้องทำให้static constอาร์เรย์นั้นมีอยู่เสมอเพื่อให้มั่นใจว่ามีอยู่เสมอ (แต่ไม่มีคำถามในภาษาเลยดังนั้นฉันจึงพยายามไม่สมมติหนึ่งคำตอบด้วยเช่นกัน )
Jerry Coffin

23

ปัญหาของif...else if...chain คือเมื่อฉันอ่านมันมาฉันต้องดูทุก ๆifเงื่อนไขเพื่อทำความเข้าใจว่าโปรแกรมกำลังทำอะไรอยู่ ตัวอย่างเช่นคุณอาจมีสิ่งนี้:

if (a == 1) {
    // stuff
} else if (a == 2) {
    // stuff
} else if (a == 3) {
    // stuff
} else if (b == 1) {
    // stuff
} else if (b == 2) {
    // stuff
}

(เห็นได้ชัดว่ามีข้อความจำนวนน้อยเช่นนี้มันไม่ได้แย่มาก)

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

ในตอนท้ายของวันที่แม้ว่าฉันไม่ต้องการค่าหรือห่วงโซ่ของswitch if...else ifบ่อยครั้งทางออกที่ดีกว่าคือตารางการกระโดดหรือพจนานุกรมบางประเภทสำหรับกรณีเช่นในคำถามดั้งเดิมหรือความหลากหลาย (ถ้าภาษาของคุณรองรับสิ่งนั้น) แน่นอนว่ามันเป็นไปไม่ได้เสมอไป แต่ฉันจะมองหาวิธีแก้ปัญหาที่หลีกเลี่ยงswitchเป็นขั้นตอนแรก ...


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

เหตุใดตารางกระโดด / พจนานุกรมจึงดีกว่าสวิตช์
Titou

14
switch (which) {
  case 0: comment = "String 1"; break;
  case 1: comment = "String 2"; break;
  case 2: comment = "String 3"; break;
  default: comment = "Oops"; break;
}

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

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


1
หากคุณใส่สวิตช์ในเมธอดและให้แต่ละreturnสตริงเป็นสตริงที่เหมาะสมคุณสามารถกำจัดbreakข้อความสั่งได้
Robert Harvey

โซลูชันของคุณเป็นวิธีที่เร็วที่สุด
Titou

8

ในกรณีนี้คำแถลงสวิทช์ชัดเจนมากขึ้นตรงกับเจตนาของรหัส: เลือกการกระทำที่จะดำเนินการตามค่าเดียว

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


8

ฉันเห็นด้วยกับ Jerry ว่าอาร์เรย์ของสตริงดีกว่าสำหรับกรณีนี้โดยเฉพาะ แต่โดยทั่วไปจะดีกว่าถ้าใช้คำสั่ง switch / case มากกว่าเชนของ elseif อื่น ๆ อ่านง่ายขึ้นและบางครั้งคอมไพเลอร์สามารถทำงานได้ดีขึ้นในการเพิ่มประสิทธิภาพด้วยวิธีนี้ แต่ก็มีประโยชน์อื่นเช่นกัน: มันเป็นเรื่องง่ายที่จะ debug

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


3

ฉันชอบสวิตช์ในกรณีเหล่านั้นมันตรงกับจุดที่ดีกว่าของรหัสเรียกใช้คำสั่งที่แตกต่างกันสำหรับแต่ละค่าอินพุตที่แตกต่างกัน การif..elseกระทำเช่น "หลอกลวง" เพื่อให้ได้ผลเช่นเดียวกัน

switch ข้อความนั้นสะอาดกว่ามันง่ายที่จะพิมพ์ตัวหนังสือที่ซ่อนอยู่ทั้งหมด ==

นอกจากนี้สำหรับบล็อกขนาดใหญ่ใน C สวิตช์จะเร็วขึ้น

else..ifอาจเหมาะสมกว่าเมื่อคุณมีบางสิ่งบางอย่างเช่นช่วง (ระหว่าง 1 ถึง 100 ทำสิ่งนี้ระหว่าง 100 ถึง 200 ทำ) หรือใน C เมื่อคุณพยายามที่จะทำให้สลับกับองค์ประกอบเช่นสตริง (ที่เป็นไปได้ในภาษาอื่น) ซึ่งเป็นแบบเดียวกัน

ฉันมักจะใช้สวิตช์จำนวนมากเมื่อฉันตั้งโปรแกรมใน C


2

เลือกสิ่งที่มีประสิทธิภาพสั้น ๆ แล้วทำเอกสารไม่ใช่แค่สิ่งที่คุณทำ แต่ทำไม

รหัสสามารถมาได้อีกครั้งและไม่ได้เป็นของผู้เขียนดั้งเดิมเสมอไป

มีหลายครั้งที่คุณอาจเลือกทำการติดตั้งหนึ่งไปอีกอันหนึ่งเนื่องจากคุณคิดไปข้างหน้าเกี่ยวกับโค้ดที่ไม่มีอยู่


2

โดยทั่วไปฉันไม่ชอบวิธีใดวิธีหนึ่ง สวิตช์ยาวหรือถ้าข้อความเพียงขอให้ refactored วัตถุที่มุ่งเน้นนามธรรม (แต่ตัวอย่างของคุณฉันจะจัดเป็นสั้นไม่ยาว)

โดยส่วนตัวฉันจะห่อโค้ดประเภทนั้นลงในวิธีการช่วยเหลือแยกต่างหาก

private string GetInsult()
{
    int which = (int)(Math.random() * 3);  //  Result is 0, 1, or 2.

    switch (which) {
        case 0: return "You look so much better than usual.";
        case 1: return "Your work is up to its usual standards.";
        case 2: return "You're quite competent for so little experience.";
        default: return "Oops -- something is wrong with this code.";
    }
}

public void Foo()
{
    string comment = GetInsult();
    Print(comment);
}

การวางสวิตช์ในวิธีแยกต่างหากช่วยให้คุณวางข้อความสั่งคืนโดยตรงภายในคำสั่งสวิตช์ (อย่างน้อยใน c #) ทำให้ไม่ต้องใช้คำสั่ง break อีกทั้งทำให้อ่านรหัสได้ง่ายขึ้น

และนี่คือสิ่งที่ดีกว่ามากถ้า / อื่น ๆ ถ้า / อื่นถ้าวิธีการ


3
ฉันเกลียดการ "วางไว้ในวิธีอื่นเพราะมันดูน่าเกลียด" ในการแก้ปัญหา รายการวิธี Clutters และดู IMHO ที่แย่ลง ฉันจะทำเช่นนี้หาก A) รหัสนั้นซ้ำที่ไหนสักแห่งหรือ B) อาจมีประโยชน์อื่น
TheLQ

1
รายการวิธีการอะไร? และทำไมรายการเมธอดของคุณจึงรกกว่าโค้ดของคุณที่รก ฉันคิดว่าเราย้ายที่ผ่านมา "เก็บทุกอย่างในวิธีการเดียวเพื่อให้คุณสามารถเห็นทุกอย่างในครั้งเดียว" อายุ
sara

@TheLQ ฉันเห็นด้วยกับคุณโดยทั่วไป แต่ในกรณีนี้ "ความคิดเห็น =" เป็นข้อเท็จจริงตามข้อเสนอของพีท
Titou

0

ในไพ ธ อนนั้นไม่มีคำสั่ง switch เพราะถ้า / elif / else ดี:

a = 5

if a==1:
    print "do this"
elif a == 2:
    print "do that"
elif a == 3:
    print "do the other"
elif 3 < a < 9:
    print "do more"
elif 9 <= a < 15:
    print "do nothing"
else:
    print "say sorry"

ง่ายใช่มั้ย


Elifเป็นเพียงคำสั่ง if ที่มีตัวอักษรหายไปสองสามตัว มันเหมือนกับifคำสั่งมากกว่าคำสั่ง switch แน่นอน ความจริงที่ Python ไม่มีสวิตช์ทำให้คนที่เกลียดพวกเขา (อย่างฉัน) คิดว่าพวกเขาไม่ได้อยู่คนเดียว
Dan Rosenstark

การจัดรูปแบบ Python ทำงานที่ stackoverflow แต่ไม่ได้อยู่ที่ programmers.stackexchange.com :(
Christopher Mahan

คุณควรเตือนพวกเขาที่metaจนกว่าจะมีหัวข้อที่เป็นที่รู้จัก ขอบคุณที่เป็นพยานให้ฉัน
Dan Rosenstark

ยาพบว่าพวกเขามีมันเกิดขึ้นที่meta.programmers.stackexchange.com/questions/308/...
คริสฮัน

1
@ ใช่เตือนฉันถึงวิกิพีเดียผู้ดูแลระบบของฉันวัน ... โอ้จอย (ฉันเป็นหัวข้อที่สมบูรณ์หรือยัง)
Christopher Mahan

0

สิ่งหนึ่งที่ทำให้รูปแบบ C / C # switchน่ารำคาญเป็นพิเศษคือการยืนยันcaseคุณค่าที่แท้จริง สิ่งหนึ่งที่ดีเกี่ยวกับ VB / VB.NET คือselect/caseให้แต่ละกรณีเป็นนิพจน์บูลีน นั่นสะดวก ตราบเท่าที่ชุดของนิพจน์บูลีนที่ไม่เกิดร่วมกันมักจะเป็นประโยชน์ชุดของ if / else ifs มีความยืดหยุ่นมากกว่าไม่พูดถึงความมีประสิทธิภาพในการพิมพ์และอ่าน

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