วันจันทร์ที่ 19 กันยายน พ.ศ. 2554

การเขียนโปรแกรมแบบเงื่อนไข

switch case

switch..case

switch..case ก็เป็นการเขียน if clause แบบหนึ่งที่ง่ายขึ้น มีรูปแบบ syntax ดังนี้
switch(<expression>) {
    case <value> : <statement>
    case <value> : <statement>
    case <value> : <statement>
    ..........................
    [default : <statement>]
}
ตรง expression นั้นจะเป็นตัวแปร หรือ method ใด ๆ ก็ได้
โดย default นั้นเปรียบเสมือน else ... ซึ่งจะเป็น option ที่จะมีก็ได้ ไม่มีก็ได้
ลองมาดูตัวอย่างเพื่อความเข้าใจดีกว่า
int grade = Int32.Parse(Console.ReadLine());
string gradeString;

switch(grade) {
    case 4 : gradeString = "A";
    case 3 : gradeString = "B";
    case 2 : gradeString = "C";
    case 1 : gradeString = "D";
    case 0 : gradeString = "F";
    default : gradeString = "Incorrect number";
}

Console.WriteLine(gradeString);
ก็ควรจะมีผลเหมือนกับ
int grade = Int32.Parse(Console.ReadLine());
string gradeString;

if(grade == 4) {
    gradeString = "A";
} else if(grade == 3) {
    gradeString = "B";
} else if(grade == 2) {
    gradeString = "C";
} else if(grade == 1) {
    gradeString = "D";
} else if(grade == 0) {
    gradeString = "F";
} else {
    gradeString = "Incorrect number";
}

Console.WriteLine(gradeString);
ถ้านำตัวอย่างนี้ไปรันในภาษาอย่าง C,C++ หรือ Java แล้วสมมุติว่าใส่ input เป็น 3 ผลที่ได้คือ
B
C
D
F
Incorrect number
นั่นเพราะว่า ถ้า case ใดตรงกับค่าใน switch expression มันก็จะทำงานที่ case อื่น ๆ ข้างล่างทั้งหมดเลย ดังนั้น C# จึงแก้ความสับสนนี้ โดยการให้ code ด้านบนนั้นไม่ผ่านการ compile

วิธีแก้ปัญหากรณีนี้ก็คือใส่ประโยค break; ลงไปในแต่ละ case ดังนี้
int grade = Int32.Parse(Console.ReadLine());
string gradeString;

switch(grade) {
    case 4 :
        gradeString = "A";
        break;
    case 3 :
        gradeString = "B";
        break;
    case 2 :
        gradeString = "C";
        break;
    case 1 :
        gradeString = "D";
        break;
    case 0 :
        gradeString = "F";
        break;
    default :
        gradeString = "Incorrect number";
}

Console.WriteLine(gradeString);
คนที่เคยเขียน pascal มาแล้วคงสงสัย ว่าทำไมต้องใส่ break ด้วยล่ะ ทำไมไม่ทำ ประโยค switch..case ให้มันทำงานแค่ case เดียวแล้วออกเลย นั่นเพราะมันมีประโยชน์บ้างครับ ลองมาดูกัน
int grade = Int32.Parse(Console.ReadLine());
string message;

switch(grade) {
    case 4 :
    case 3 :
        message = "You 're good";
        break;
    case 2 :
    case 1 :
        message = "You should pratice more";
        break;
    case 0 :
        message = "try try and try";
        break;
    default :
        message = "Incorrect number";
}

Console.WriteLine(message);
นั่นคือถ้าได้เกรด 4 หรือเกรด 3 message ก็จะเป็นข้อความว่า You 're good แต่ถ้าได้ 2 หรือ 1 ก็จะเป็นข้อความว่า You should pratice more

*ในภาษาอื่น ๆ นั้นตรง expression นั้นต้องเป็น integral type เท่านั้น นั่นคือจะต้องเป็นพวก int short,... หรือ char แต่ว่าใน C# นั้นสามารถใส่ expression เป็น string ได้ด้วย
string gradeString = Int32.Parse(Console.ReadLine());
string message;

switch(gradeString.ToUpper()) {
    case "A" :
        message = "Excellent";
        break;
    case "B" :
        message = "Good";
        break;
    case "C" :
        message = "Cool";
        break;
    case "D" :
        message = "Try";
        break;
    case "F" :
        message = "Get out!!";
        break;
    default :
        message = "Incorrect grade";
}

Console.WriteLine(message);

Formatting String

คุ้น ๆ code ด้านล่างนี้ใช่มั้ยครับ
Console.WriteLine("Hi {0}\nYou 're {1} years old", name, age);
มันมาจาก tutorial ตอนที่ 1 เป็นสไตล์การเขียนแบบ C ครับ ถ้าเป็นใน C ก็คงเป็นอย่างนี้
printf("Hi %s\nYou 're %s years old\n", name, age);
โดย %s บอกว่าให้เอา string มาใส่ตรงนี้นะ(s คือ string ถ้าเป็น int ก็จะเป็น %d, float ก็ %f, char ก็ %c)

การเขียนอย่างนี้ไม่ได้ช่วยให้การอ่านประโยคนั้นต่อเนื่องเพียงอย่างเดียว แต่มันยังสามารถจะ format ได้อีกด้วย ลองดูตัวอย่างกัน
Console.WriteLine("{0,5}{1,5}", 123, 456);     // Right-aligned
Console.WriteLine("{0,-5}{1,-5}", 123, 456);   // Left-aligned
ผลลัพธ์
  123  456
123  456
อันนี้คือการจัดชิดซ้ายชิดขวา โดยการระบุความกว้าง(จำนวนตัวอักษร)ลงไป
ถ้าเป็น C จะเขียนอย่างนี้ (ใน C ถ้าจะขึ้นบรรทัดใหม่ ต้องใส่ '\n' เองเสมอ)
printf("%5d%5d\n", 123, 456);     // Right-aligned
printf("%-5d%-5d\n", 123, 456);   // Left-aligned
ถ้าเป็น Pascal
writeln(123:5, 456:5); { Right-aligned }
writeln(123:-5, 456:-5); { Left-aligned }
ประโยชน์ที่เห็นได้จากการจัดความกว้างก็เช่นการโชว์ข้อมูลเป็นบล็อก ๆ ให้ตรงกัน เพื่อความสวยงาม
Console.WriteLine("{0,-10}{1,-3}", "Name","Salary");
Console.WriteLine("----------------");
Console.WriteLine("{0,-10}{1,6}", "Bill", 123456);
Console.WriteLine("{0,-10}{1,6}", "Polly", 7890);
Name      Salary
----------------
Bill      123456
Polly       7890

นอกจากการกำหนดความกว้างแล้ว เรายังกำหนดรูปแบบได้อีกด้วย โดยการใส่ :(colon) แล้วตามด้วยตัวอักษรลงไปหลังตัวเลขระบุตำแหน่ง ดังนี้

Characterความหมาย
C หรือ cCurrency(เงิน)
D หรือ dDecimal(แปลว่าเลขฐาน 10 นะ อย่าสับสนกับ ตัวแปรประเภท decimal)
E หรือ eExponent(ระบบวิทยาศาสตร์)
Fหรือ fFloating poing(ทศนิยม)
G หรือ gGeneral
N หรือ nNumber(เหมือน F แต่จะใส่ comma คั่นตัวเลขทุก ๆ 3 หลัก
P หรือ pPercentage
R หรือ rRound-trip ใช้กับ floating-point value เท่านั้น โดยจะ guaruntee ว่าค่าที่ถูกเปลี่ยนไปจะมีค่าเท่ากับตัวเดิม
X หรือ xHexadecimal(เลขฐาน 16)

int i = 123456;
Console.WriteLine("{0:C}", i);
Console.WriteLine("{0:D}", i);
Console.WriteLine("{0:E}", i);
Console.WriteLine("{0:F}", i);
Console.WriteLine("{0:G}", i);
Console.WriteLine("{0:N}", i);
Console.WriteLine("{0:P}", i);
Console.WriteLine("{0:X}", i);
฿123,456.00
123456
1.234560E+005
123456.00
123456
123,456.00
12,345,600.00 %
1E240


Console.WriteLine("{0:D6} {1:D4}", 123, 456);
000123 0456

เราสามารถรวมการจัดความกว้างและฟอร์แม็ตไปพร้อม ๆ กันได้ดังนี้
Console.WriteLine("{0,-10:D6}{1,-10:F4}", 123, 456.0000);
Console.WriteLine("{0,-12:N2}{1,9:X4}", 1234567, 240);
000123     456.0000
1,234,567.00   00F0

อ้อ ลองมาดูความแตกต่างของ F กับ R กันตรงนี้เลยครับ
double d = 1.2345678901234567890;
Console.WriteLine("{0:F16}", d);
Console.WriteLine("{0:R16}", d);
1.2345678901234600
1.2345678901234567
ตาม definition ของ R เป็นอย่างนี้