Python by exercises:finishing

From Blognone

Jump to: navigation, search

Contents

ฟังก์ชัน

จากแบบฝึกหัดข้อที่ผ่านมา ถ้าข้อมูลเกรดในแฟ้มดังกล่าวถูกเก็บเป็นสตริง เช่น C+ หรือ B ในการคำนวณเกรดเฉลี่ยเราจำเป็นต้องแปลงสตริงให้เป็นค่าแต้มคะแนน อย่างไรก็ตามในภาษา Python ไม่มีโครงสร้าง switch เราจึงจำเป็นต้องเขียนส่วนของโปรแกรมดังเช่นด้านล่าง

อย่างไรก็ตามเราสามารถใช้ข้อมูลแบบพจนานุกรมเพื่อทำให้โปรแกรมอ่านง่ายขึ้นได้ เช่น
<source lang="python">
g = raw_input()
scores = {'A':4, 'B+':3.5, 'B':3, 'C+':2.5, 
                 'C':2, 'D+':1.5, 'D':1, 'F':0 }
print scores[g]

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

def convert_score(s):
    scores = {'A':4, 'B+':3.5, 'B':3, 'C+':2.5, 
              'C':2, 'D+':1.5, 'D':1, 'F':0 }
    return scores[s]

เมื่อเราเรียกใช้ convert_score('B') เราจะได้ผลลัพธ์เป็น 3 (สังเกตว่าถ้าเราเขียนข้อมูลในวงเล็บ เราสามารถใช้ย่อหน้าอย่างไรก็ได้ แต่คำสั่ง return ต้องย่อหน้าตรงกับบรรทัด scores = ...)

แบบฝึกหัด: คำนวณเกรดเฉลี่ยทั้งหมด

แฟ้ม grades.txt เก็บเกรดของวิชาต่าง ๆ ของนักศึกษา ในแต่ละบรรทัดของแฟ้มดังกล่าว จะมีข้อมูลในรูปแบบ

รหัสนิสิต,รหัสวิชา,เกรด

โดยเกรดจะเป็นรหัสคะแนน ('A' ถึง 'F') ข้อมูลในแฟ้มดังกล่าวอาจจะเรียงตามลำดับอย่างไรก็ได้ (ไม่จำเป็นต้องเรียงตามรหัสนิสิตหรือรหัสวิชาแต่อย่างใด)

ให้เขียนโปรแกรม compute_all_gpa.py ที่อ่านแฟ้มดังกล่าวและแฟ้ม courses.txt จากนั้นคำนวณเกรดเฉลี่ยของนิสิตทุกคน โดยพิมพ์ผลลัพธ์ออกมาในรูปแบบ

รหัสนิสิต,เกรดเฉลี่ย

ในโปรแกรมของแบบฝึกหัดนี้ ให้ใช้ฟังก์ชัน convert_score ข้างบนและเขียนฟังก์ชัน build_credits ที่อ่านแฟ้ม courses.txt แล้วคืนค่าเป็นพจนานุกรมดังที่อธิบายในส่วนพจนานุกรม

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

หมายเหตุ ลองดูตัวอย่างด้านล่างในการจัดรูปแบบการพิมพ์ โดยใช้ operator % และใส่ข้อมูลในทูเปิล

>>> print 50051111,1.23
50051111 1.23
>>> print 50051111,',',1.23
50051111 , 1.23
>>> print '%s,%s' % (50051111,1.23)      # เราต้องการรูปแบบนี้
50051111,1.23

แบบฝึกหัด: ฟังก์ชันอ่านแฟ้มรูปแบบข้อมูลคั่นด้วยเครื่องหมายลูกน้ำ

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

def readcsvfile(fname):
    # ใส่โปรแกรมของคุณตรงนี้
 
data = readcsvfile('courses.txt')
print data

ผลลัพธ์เมื่อเรียกใช้งาน (สมมติว่าเก็บโปรแกรมในแฟ้ม readcsv.py)

$ python readcsv.py               # or c:\> python readcsv.py
[['417167', '4'], ['420111', '3'], ['420113', '1'], ... , ['402114', '1']]

คำใบ้ การเพิ่มข้อมูลเข้าไปท้ายลิสต์ใช้ฟังก์ชัน append

แบบฝึกหัด: คำนวณเกรดเฉลี่ยของทุกคน (แยกฟังก์ชัน)

แก้โปรแกรมในแบบฝึกหัดคำนวณเกรดเฉลี่ยทุกคน โดยให้เขียนหรือใช้ฟังก์ชันดังต่อไปนี้

  • convert_score(g) ดังส่วนฟังก์ชันข้างต้น
  • readcsvfile(fname) ที่เขียนในแบบฝึกหัด
  • build_credits(course_data) แก้ฟังก์ชันที่ทำในแบบฝึกหัดก่อนหน้า ให้ไม่ต้องอ่านแฟ้ม courses.txt โดยตรง แต่รับข้อมูลที่ได้จากการอ่านด้วยฟังก์ชัน readcsvfile แทน
  • compute_gpa(credits,grade_data) รับข้อมูลที่อ่านจากแฟ้ม courses.txt และ grades.txt ด้วยฟังก์ชัน readcsvfile จากนั้นคำนวณ gpa โดยคืนค่าเป็น dictionary ที่มีกุญแจเป็นรหัสประจำตัวนิสิต

โดยส่วนของโปรแกรมหลักที่เรียกใช้ฟังก์ชันต่าง ๆ ให้ใช้ตามด้านล่างนี้

course_data = readcsvfile('courses.txt')
credits = build_credit(course_data)
grade_data = readcsvfile('grades.txt')
gpa = compute_gpa(credits,grade_data)
 
for id in gpa.keys():
    print id, gpa[id]

ตัวอย่างผลลัพธ์เมื่อเรียกใช้งาน

50000043 2.53703703704
50000042 1.51851851852
50000041 2.44444444444
...                           #ละไว้

แบบฝึกหัด: อ่านลำดับการเลือก

เขียนฟังก์ชัน build_pref ที่ใช้ข้อมูลที่ได้จากการอ่านลำดับการเลือกที่อยู่ในแฟ้ม preferences.txt ผ่านทางฟังก์ชัน readcsvfile แล้วคืนค่าข้อมูลแบบพจนานุกรมของลิสต์ในลักษณะที่จะได้อธิบายต่อไป

ในแต่ละบรรทัดของแฟ้มดังกล่าว จะมีข้อมูลในรูปแบบ

รหัสนิสิต,อันดับการเลือก,รหัสภาควิชา

โดยอันดับการเลือกจะมีค่าตั้งแต่ 1 ถึง 8 (มี 8 ภาควิชา) ข้อมูลในแฟ้มเรียงลำดับตามเลขประจำตัวและอันดับการเลือก

ค่าที่ฟังก์ชันดังกล่าวคืนมาจะเป็นพจนานุกรมที่มีกุญแจเป็นรหัสประจำตัวนิสิต มีค่าเป็นรายการของภาควิชาเรียงตามลำดับการเลือก

ตัวอย่างเช่น ถ้าในแฟ้มมีข้อมูล

1234,1,204
1234,2,210
1234,3,208
5678,1,208
5678,2,204
5678,3,210

ค่าที่คืนจากฟังก์ชันจะเป็น {'1234':['204','210','208'], '5678':['208','204','210']}

ทดลองนำไปใช้กับโปรแกรมหลักด้านล่างนี้

pref_data = readcsvfile('preferences.txt')
pref = build_pref(pref_data)
 
for id in pref.keys():
    print '(%s)' % (id,),
    for r in pref[id]:
        print r,
    print

สังเกตว่าเราเขียน (id,) เพื่อระบุข้อมูลทูเปิลที่มีสมาชิกหนึ่งตัว เราใส่เครื่องหมายลูกน้ำ เพื่อแยกแยะระหว่าง (id) กับ (id,)

ตัวอย่างผลลัพธ์เมื่อเรียกใช้งาน

(50000043) 215 206 213 204 203 202 205 208
(50000042) 204 213 203 208 206 202 205 215
...                           #ละไว้

คำสั่งและการใช้งาน Python เพิ่มเติม

การกำหนดค่าแบบขนาน

ใน Python เราสามารถสั่งกำหนดค่าแบบขนาน (คือการกำหนดค่าตัวแปรหลายตัวพร้อมกัน) ได้ ดังเช่น

>>> a,b,c = 1,2,3              # a=1, b=2, c=3
>>> e,f,g = a,b,c              # e=a=1, f=b=2, g=c=3
>>> a,b = b,a                  # a=2, b=1 (สลับค่า) 
>>> a,b = [1,2]                # unpack: a=1, b=2
>>> a,b,c = [1,2,[3,4]]        # unpack: a=1, b=2, c=[3,4]
>>> [a,b,[c,d]] = [1,2,[3,4]]  # unpack: a=1, b=2, c=3, d=4

ในตัวอย่างตอนต้นหลายตัวอย่าง เราอาจเขียนคำสั่ง id,course,grade = items หรือกระทั่ง id,course,grade = line.split(',') เพื่อแยกข้อมูลจากลิสต์มาใส่ในตัวแปรย่อย ๆ ได้

ฟังก์ชัน range

เราอาจสงสัยว่าคำสั่งวนรอบเช่น for(i=0; i<10; i++) ในภาษา C จะนำมาเขียนใน Python ได้อย่างไร เราจะใช้ฟังก์ชัน range ซึ่งรับค่าเป็นจำนวนเต็มแล้วคืนค่าเป็นลิสต์ที่เริ่มตั้งแต่ 0 จนถึงค่าจำนวนเต็มดังกล่าวลบหนึ่ง เราจะใช้ฟังก์ชันดังกล่าวประกอบกับคำสั่ง for ตัวอย่างเช่น

>>> for i in range(10):
...     print i,
0 1 2 3 4 5 6 7 8 9

หมายเหตุ: จริง ๆ แล้วฟังก์ชันดังกล่าวไม่ได้สร้างลิสต์ขึ้นมาทั้งหมดในครั้งเดียว

การจัดเรียงข้อมูล

ลิสต์มีเมท็อดที่สำคัญคือเมท็อด sort ในการจัดเรียงข้อมูล เมท็อดดังกล่าวทำลายค่าในลิสต์เดิม นอกจากนี้เมท็อดดังกล่าวยังไม่คืนค่าลิสต์ออกมาด้วย พิจารณาตัวอย่างการใช้งาน

>>> a = [1,4,3,2,5]
>>> a.sort()
>>> a                                         #> [1, 2, 3, 4, 5]
>>> b = [1,5,4,3,2]
>>> for x in b.sort():
...     print x
... 
Traceback (most recent call last):            # เมท็อด sort ไม่คืนค่า
  File "<stdin>", line 1, in <module>         #
TypeError: 'NoneType' object is not iterable  #

ถ้าต้องการฟังก์ชันที่คืนค่าและไม่เปลี่ยนค่าในลิสต์ให้ใช้ฟังก์ชัน sorted แทน ดังตัวอย่างต่อไปนี้

>>> c = [1,5,4,3,2]
>>> for x in sorted(c):
...     print x
1
..                                            # ละไว้
5
>>> c                                         #> [1, 5, 4, 3, 2]

ในกรณีที่ข้อมูลในลิสต์ไม่ใช่จำนวน (เช่นเป็นทูเปิล) คำสั่งจัดเรียงดังกล่าวจะเรียงโดยอิงจากหลักหน้าไปก่อน เช่น (1,2) น้อยกว่า (2,1) หรือ (2,4) น้อยกว่า (2,5) เป็นต้น

แบบฝึกหัด: โปรแกรมจัดอันดับการเข้าสาขา

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

อ่านข้อมูลเกี่ยวกับแต่ละภาควิชาได้จากแฟ้ม departments.txt แฟ้มดังกล่าวเก็บข้อมูลภาควิชาละหนึ่งบรรทัด โดยเริ่มจากรหัสภาควิชา ชื่อภาควิชา และจำนวนรับ (มากที่สุด)

ให้โปรแกรมพิมพ์ผลลัพธ์ในรูปแบบดังนี้

ชื่อภาควิชา                 (ตามลำดับในแฟ้ม departments.txt)
รหัสประจำตัว เกรดเฉลี่ย      (เรียงตามลำดับจากมากไปน้อยจนหมด)
รหัสประจำตัว เกรดเฉลี่ย
...
ชื่อภาควิชา
รหัสประจำตัว เกรดเฉลี่ย      (เรียงตามลำดับจากมากไปน้อยจนหมด)
รหัสประจำตัว เกรดเฉลี่ย
...