Saturday, May 8, 2010

Python Programming

ส่งท้าย while loop และตัวแปร list

ประพัฒน์ สุริยผล


ครั้งนี้ตั้งใจจะว่ากันเรื่อง list แต่อดไม่ได้ที่จะขอย้อนกลับไปพูดเรื่อง while loop ต่อจากครั้งที่แล้วอีกนิดหน่อย


จากครั้งที่แล้ว code ที่ทำกันออกมา ใช้ดีทีเดียว บาง code ก็ทำงานได้ บาง code ก็ผิดพลาดบ้าง แล้วบาง code ก็ทำให้ผมอมยิ้มกับไอเดียเก๋ไก๋ในการเลือก condition สำหรับ while loop


ถ้าเราคิดอย่างตรงไปตรงมา condition ของ while loop น่าจะเป็นคำพูดที่ว่า


“ทำต่อไปเรื่อยๆ ตราบใดที่ choice ไม่เท่ากับ Q” เพราะเราต้องการให้วนลูปจนกว่าผู้ใช้จะต้องการหยุด ด้วยการกดปุ่ม Q


เราพอจะเขียนเป็น pseudocode ได้ประมาณนี้


while choice.lower() != "q":

____ทำสิ่งที่เราต้องการให้ loop ทำ

print "Thank you for using my program"


จากโครงสร้างที่ได้ ก็จะเห็นชัดว่า ตรงไหนเป็นส่วน ลูป ตรงไหนไม่อยู่ในลูป (บรรทัดสุดท้าย)


ปัญหาคือ code ที่ใช้รับค่าของ choice (ในคำสั่ง raw_input) อยู่ใน loop แต่เราเรียกใช้มันใน condition โปรแกรม python ก็จะโวยวายว่า เราใช้ variable โดยที่ยังไม่ได้กำหนดค่า


เราแก้ไขได้ง่ายๆ ด้วยการให้ค่า choice เป็นอะไรก็ได้ที่ไม่ใช่ q เพื่อให้เข้าไปใน loop รอบแรกได้ โดยไม่มีปัญหา เมื่อเข้าไปใน loop แล้ว โปรแกรมของเราก็จะถามค่า choice ตามปกติ pseudocode จึงออกมาเป็น


choice = ""

while choice.lower() != "q":

____ทำสิ่งที่เราต้องการให้ loop ทำ

print "Thank you for using my program"


แต่วิธีนี้ มีข้อเสียอยู่ คือ เมื่อเราใส่ q เพื่อออกจาก loop นอกจากโปรแกรมจะพิมพ์ขอบคุณที่ใช้งานแล้ว ยังพิมพ์โวยวายออกมาก่อนว่าให้เราใส่ค่า r/t/c/z เสียอีก ที่เป็นอย่างนี้ เพราะว่าตัว q ไม่อยู่ในเงื่อนไขของ if และ elif ที่เรามีอยู่ โปรแกรมจึงทำงานในส่วนที่เป็น else ที่พิมพ์ค่าเตือนผู้ใช้ออกมา


ทางแก้คือเพิ่ม elif ตัว q ลงไป ดังนี้


choice = ""

while choice.lower() != "q":

____ทำสิ่งที่เราต้องการให้ loop ทำ

____...

____elif choice.lower() == "q":

________pass

____else:

________print ....

print "Thank you for using my program"


อันนี้ จะเห็นว่าเราใช้ keyword ตัวหนึ่งชื่อ pass เพื่อบอก python ว่าไม่ต้องทำอะไร ผ่านไปเฉยๆ


แต่วิธีนี้ก็ยังมีข้อเสียอยู่ เพราะเราเห็น code ที่ซ้ำซ้อนกันชัดเจน ก็คือ condition ที่เปรียบเทียบค่า choice สองที่ ตรง while condition และ if condition ดูไม่ดีเลย แถมยังต้องกำหนดค่า choice ก่อนเข้า while loop อีก


อันที่จริง เราควรจะกำหนดให้วนลูปไปเรื่อยๆ เมื่อผู้ใช้กด q จึงค่อยหลุดออกจากลูป (break from the loop) ซึ่งเราสามารถทำได้ด้วยการใช้ keyword break ดังนั้น pseudocode ที่ได้จึงเป็น


while True:

____ทำสิ่งที่เราต้องการให้ loop ทำ

____...

____elif choice.lower() == "q":

________break

____else:

________print ....

print "Thank you for using my program"


เพียงเท่านี้ เราก็ได้โปรแกรมที่ทำงานตามที่ต้องการ ข้อสังเกตคือตรง condition ของ while เราต้องการให้เป็นจริงเสมอ ซึ่งเราสามารถทำได้ด้วยการใช้ keyword True โดยไม่จำเป็นต้องสร้างค่าตัวแปรใดให้วุ่นวาย


แบบฝึกหัด


ปรับโปรแกรมให้ใช้ condition True และใช้ break แล้วลองเปรียบเทียบกับโปรแกรมที่เคยเขียนไว้ก่อนหน้านี้ (ส่วนใครที่เขียนแบบนี้แล้วตั้งแต่แรก ก็ขอแสดงความยินดีด้วยครับ คุณมาได้ถูกทางแล้ว)


List


เราเคยพูดถึงตัวแปรที่เก็บค่าต่างๆ เช่น ตัวเลขจำนวนเต็ม เลขทศนิยม และตัวอักษรกันมาแล้ว


ในกรณีของตัวแปรที่เก็บตัวอักษร (string) เราสามารถมองได้ในอีกรูปแบบหนึ่งว่าประโยคที่อยู่ในตัวแปรนั้น ประกอบด้วยตัวอักษรต่างๆ มาเรียงตัวกัน อย่างเช่น


test = "hello"


ตัวแปร test เก็บค่าลำดับของตัวอักษรไว้ 5 ตัวคือ h, e, l, l และ o


ตัวแปรที่มีลักษณะแบบลิสต์นี้ เราสามารถอ่านตัวอักษรที่อยู่ในลิสต์ได้ทีละตัว ด้วยการกำหนดค่า index โดยในโปรแกรม python ค่า index จะเริ่มนับจากเลข 0 ตัวแปร test มี 5 ตัวอักษร index จึงมีได้ตั้งแต่เลข 0 ถึง 4 โดยมีรูปแบบการใช้ index ดังนี้


ชื่อตัวแปร ตามด้วยเลข index ที่อยู่ในเครื่องหมายวงเล็บใหญ่


เพราะฉะนั้น ถ้าเราใช้คำสั่ง


>>> test = “hello”

>>> print test[0]

h

>>> print test[1]

e

>>> print test[4]

o


เราจะทราบได้อย่างไรว่า ลิสต์นั้นเก็บค่าไว้ทั้งหมดกี่ค่า คำตอบคือเราสามารถทำได้ ด้วยการใช้ฟังก์ชัน len()


>>> print len(test)

5

>>> test1 = “goodbye”

>>> print len(test1)

7


มาลองดูว่าเราจะใช้ list ให้เป็นประโยชน์กันได้อย่างไร สมมติว่าเรามี DNA sequence อยู่หนึ่งเส้น และเราต้องการนับจำนวน G และ C ว่ามีเท่าไหร่ คิดเป็นกี่เปอร์เซ็นต์ เราสามารถเขียนได้ดังนี้


1 dna = rawinput("Please enter DNA seq")

2 num_g = 0

3 num_c = 0

4 for base in dna:

5 if base == "G":

6 num_g += 1

7 if base == "C":

8 num_c += 1

9 print "number of G =", num_g

10 print "number of C =", num_c

11 print "total number of G+C =", num_g + num_c

12 print "percentage of G+C =" (num_g + num_c) * 100 / len(dna)


บรรทัดแรกเรารับสาย DNA จากผู้ใช้

บรรทัดที่ 2-3 เป็นการกำหนดตัวแปรเพื่อเก็บค่าที่เราต้องการนับ


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


for ตัวแปรที่จะใช้ในลูป in ตัวแปรลิสต์:


หลังจากนั้น ทุกอย่างที่อยู่ในย่อหน้าภายใต้คำสั่ง for จะสามารถนำตัวแปรที่จะใช้ในลูปไปใช้ได้


ครั้งแรกที่เข้าลูป ตัวอักษรตัวแรกในตัวแปร dna จะเข้าไปอยู่ในตัวแปร base สมมติว่าเราให้ข้อมูล AGTC

ตอนที่โปรแกรมถาม เข้าลูปครั้งแรก ตัวแปร base จะมีค่าเป็น A ซึ่งจะไม่ตรงตามเงื่อนไขของบรรทัดที่ 5 โปรแกรมจะกระโดดไปทำงานที่บรรทัดที่ 7 ซึ่งก็ไม่ตรงเงื่อนไขอีก จึงกระโดดไปทำงานถัดไป แต่ว่าไม่มีประโยคใดเหลืออีกแล้วในย่อหน้านี้ โปรแกรมจึงกลับไปที่บรรทัดที่ 4 ใหม่ของลูป for ตัวอักษรตัวถัดไปก็จะเข้าไปอยู่ในตัวแปร base ในกรณีจะเป็น G มาที่บรรทัดที่ 5 ก็จะพบว่าเงื่อนไขเป็นจริง โปรแกรมไปทำงานต่อที่บรรทัดที่ 6 เพิ่มค่าในตัวแปร num_g ให้กลายเป็น 1 แล้วก็ไปทำงานต่อที่บรรทัดที่ 7 ซึ่งไม่จริง จึงข้ามบรรทัดที่ 8 ไป แล้วกลับไปที่บรรทัดที่ 4 ใหม่ ได้ค่า T อยู่ในตัวแปร base แล้วทำงานวนเช่นนี้ไปเรื่อยๆ จนกระทั่งตัวแปร base มีค่า C เมื่อเสร็จสิ้นการทำงานในรอบนี้แล้ว เมื่อโปรแกรมกระโดดกลับไปทำงานที่บรรทัดที่ 4 พบว่าไม่มีค่าอะไรที่สามารถใส่ให้ตัวแปร base อีกแล้ว ก็จะเป็นการจบลูป โปรแกรมจะกระโดดไปทำงานที่บรรทัดที่ 9


บรรทัดที่ 9-12 เป็นการพิมพ์ผลลัพธ์


แบบฝึกหัด


เขียนโปรแกรมเพื่อตรวจสอบดูว่า DNA sequence ที่ผู้ใช้ใส่เข้ามานั้น valid หรือไม่ ซึ่งจะ valid ได้นั้น คือมี base ได้แค่ A, T, G , C หรือ N โดยที่ตัวอักษรจะเป็นตัวเล็กหรือใหญ่ก็ได้

2 comments:

  1. อาจารย์ประพัฒน์ครับ
    ขออนุญาต comment นะครับ

    บรรทัดนี้อ่าคับ
    dna = rawinput("Please enter DNA seq")
    น่าจะเป็น
    dna = raw_input("Please enter DNA seq")
    ไม่แน่ใจว่าอาจารย์ลืมเติม _ ระหว่าง raw กับ input หรือป่าวคับ

    ขอบคุณนะครับ

    ReplyDelete
  2. ขออนุญาตอีก comment นึงนะคับ
    ตัวอย่างข้างบน data type มันเป็น string อ่าคับ
    ผมคิดว่าไม่ควรจะให้คนอ่าน มองมันเป็น list อ่าคับ
    เพราะอาจจะสับสนตอนใช้ method (function) ได้อ่าครับ

    เช่น ถ้าเราต้องการที่จะใช้ function reverse()
    ถ้าตัวแปรที่เป็น string แล้ว มันจะใช้ไม่ได้อ่าคับ
    จะใช้ได้สำหรับ list อย่างเดียว

    อีกข้อนึง ในตัวอย่างข้างบน ไม่แน่ใจว่าถ้าเราใช้ function count()
    จะไวกว่ามาเปรียบเทียบทีละตัวหรือป่าวครับ
    dna.count('G')
    dna.count('C')

    (comment ทั้งหมดเป็นความเห็นส่วนตัวอ่าครับ
    ถ้าผิดพลาดประการใดก็ขออภัยด้วยครับ
    ยังมือใหม่อยู่ครับ)

    ปล. อยากรบกวนให้อาจารย์สอนพวก object ด้วยก็จะเป็นประโยชน์มากครับ
    ขอบพระคุณมากครับ

    ReplyDelete