ไฟล์ในโครงการรับนักเรียนเข้าฝึกทักษะวิจัย ณ สวทช.

ไฟล์ทั้งหมดที่ใช้และสร้างที่เกี่ยวข้องขณะเข้าร่วมการฝึกทักษะวิจัยที่ NECTEC สวทช.

View the Project on GitHub ptsgrn/nstda-nectec-summer-2022

View My GitHub Profile

22/03/2022

โดย Prachya Boonkwan and Patsagorn Yuenyong, CC-BY-SA-3.0

โครงสร้างข้อมูลในภาษา Python — https://tinyurl.com/ybn763lq

ปรัชญา บุญขวัญ

Email: prachya.boonkwan@nectec.or.th

(C) December 2018

สารบัญ


กิจกรรม 9: ลิสต์ (List)

ทบทวน

scores = [35.5, 73.0, 83.5, 65.0, 81.0, 79.5, 87.0, 49.5]
print(scores, end='\n\n')

print('Finding the first high-score...')
begidx = 0
for i in range(len(scores)):
    print('Score:', scores[i])
    if scores[i] > 80.0:
        begidx = i
        break
print('First high-score index:', begidx, end='\n\n')

print('Finding the last high-score...')
endidx = len(scores)
for i in range(len(scores) - 1, -1, -1):
    print('Score:', scores[i])
    if scores[i] > 80.0:
        endidx = i
        break
print('Last high-score index:', endidx, end='\n\n')

print('Score slice:', scores[begidx : endidx + 1])
[35.5, 73.0, 83.5, 65.0, 81.0, 79.5, 87.0, 49.5]

Finding the first high-score...
Score: 35.5
Score: 73.0
Score: 83.5
First high-score index: 2

Finding the last high-score...
Score: 49.5
Score: 87.0
Last high-score index: 6

Score slice: [83.5, 65.0, 81.0, 79.5, 87.0]

การเพิ่มสมาชิกในลิสต์ด้วยคำสั่ง append

my_numbers = [11, 12, 13, 14, 15]

for i in range(1, 6):
    my_numbers.append(i)
    
print(my_numbers)
[11, 12, 13, 14, 15, 1, 2, 3, 4, 5]

แบบฝึกหัด 9.1

จงนำสมาชิกแต่ละตัวในลิสต์ new_numbers ไปต่อท้ายลิสต์ my_numbers ด้วยคำสั่ง append และแสดงลิสต์ my_numbers ออกหน้าจอ

my_numbers = [1, 2, 3, 4, 5]
new_numbers = [11, 12, 13, 14, 15]

for element in new_numbers:
    my_numbers.append(element)      # แก้ไขโค้ดที่นี่
    
print(my_numbers)
[1, 2, 3, 4, 5, 11, 12, 13, 14, 15]

การเพิ่มสมาชิกในลิสต์ด้วยคำสั่ง insert

my_numbers = [20, 40, 60, 80, 100]
my_numbers.insert(2, 70)
print(my_numbers)
[20, 40, 70, 60, 80, 100]

แบบฝึกหัด 9.2

จงเพิ่มสมาชิกใหม่ลงในลิสต์ my_numbers ตามรายการดังต่อไปนี้

  1. เลข 50 ที่ index 1
  2. เลข 70 ที่ index 3
  3. เลข 90 ที่ index 5

แล้วจึงแสดงลิสต์ my_numbers ออกที่หน้าจอ

my_numbers = [20, 40, 60, 80, 100]

# แทรกเลข 50 ที่ index 1
my_numbers.insert(1, 50)      # แก้โค้ดที่นี่

# แทรกเลข 70 ที่ index 3
my_numbers.insert(3,70)      # แก้โค้ดที่นี่

# แทรกเลข 90 ที่ index 5
my_numbers.insert(5,90)      # แก้โค้ดที่นี่

print(my_numbers)
[20, 50, 40, 70, 60, 90, 80, 100]

ผลลัพธ์ที่ได้ควรจะเป็น [20, 50, 40, 70, 60, 90, 80, 100] นะครับ

แบบฝึกหัด 9.3

หากเราแทรกตัวเลข 11 ถึง 15 เข้าไปที่ต้นลิสต์ my_numbers (คือ index ที่ 0) เราจะได้ผลลัพธ์ดังนี้

my_numbers = [1, 2, 3, 4, 5]
new_numbers = [11, 12, 13, 14, 15]

for i in new_numbers:
    my_numbers.insert(0, i)
    
print(my_numbers)
[15, 14, 13, 12, 11, 1, 2, 3, 4, 5]

จะสังเกตได้ว่า ตัวเลข 11 ถึง 15 ที่ด้านบนยังคงเรียงลำดับจากมากไปน้อยอยู่ เรามาแก้ไขโปรแกรมด้านบนเพื่อให้ตัวเลข 11 ถึง 15 เรียงลำดับจากน้อยไปมากกันดีกว่าครับ

my_numbers = [1, 2, 3, 4, 5]
new_numbers = [11, 12, 13, 14, 15]

for i in range(len(new_numbers)):
    my_numbers.insert(i, new_numbers[i])      # แก้ไขโค้ดที่นี่
    
print(my_numbers)
[11, 12, 13, 14, 15, 1, 2, 3, 4, 5]

ผลลัพธ์ที่ได้ควรจะเป็นดังนี้ครับ [11, 12, 13, 14, 15, 1, 2, 3, 4, 5]

แบบฝึกหัด 9.4: Insertion Sort

จงเขียนโปรแกรมเพื่อเรียงลำดับข้อมูลด้วยแอลกอริธึม Insertion Sort

หลักการของ insertion sort เปรียบเสมือนการเรียงไพ่ในมือ เวลาที่เราเล่นไพ่ หลังจากที่เราจั่วไพ่ขึ้นมา เราจะนำไพ่ใบนั้นมาเทียบกับไพ่แต่ละใบในมือ หากไพ่ที่จั่วได้มามีค่าน้อยกว่าไพ่ใบไหนในมือ เราก็จะนำไพ่ใบใหม่ไปแทรกที่ตำแหน่งนั้น

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

ในแบบฝึกหัดนี้เราจะมาเขียนโปรแกรมเรียงลำดับตัวเลข โดยใช้หลักการเดียวกันการเรียงไพ่ในมือนะครับ

# กองไพ่
items = [3, 8, 5, 10, 2, 9, 7, 6, 3, 8, 1, 4]

# ไพ่ที่อยู่ในมือ
result = []

# จั่วไพ่จากกองออกมาทีละใบ
for new_item in items:
    
    # ตัวแปรนี้มีไว้ตรวจสอบว่า เราแทรกไพ่ที่จั่วมาลงในมือได้หรือยัง
    is_inserted = False
    
    # ไล่เทียบไพ่ที่จั่วได้กับไพ่แต่ละใบในมือ โดยตัวแปร j จะแทนตำแหน่งไพ่แต่ละใบในมือ
    for j in range(len(result)):
        
        # ถ้าไพ่ที่จั่วได้มีหน้าไพ่น้อยกว่าหรือเท่ากับไพ่ในมือ
        if new_item < result[j]:                          # แก้ไขโค้ดที่นี่
            
            # ให้แทรกไพ่ที่จั่วได้ลงไปที่ตำแหน่งดังกล่าว แล้วบันทึกไว้ว่าแทรกไพ่ได้แล้ว
            result.insert(j, new_item)          # แก้ไขโค้ดที่นี่
            is_inserted = True
            break

    # ถ้าหากเทียบจนครบทุกใบในมือแล้ว ยังแทรกไพ่ไม่ได้เลย
    if not is_inserted:
        
        # ให้นำไพ่ที่จั่วได้ไปต่อท้ายไพ่ที่อยู่ในมือแทน
        result.append(new_item)              # แก้ไขโค้ดที่นี่
            
print(result)
[1, 2, 3, 3, 4, 5, 6, 7, 8, 8, 9, 10]

การเพิ่มสมาชิกในลิสต์ด้วยคำสั่ง extend

my_list = [1, 2, 3, 4, 5]
new_list = [11, 12, 13, 14, 15]

my_list.extend(new_list)
print(my_list)
[1, 2, 3, 4, 5, 11, 12, 13, 14, 15]

แบบฝึกหัด 9.5

จงนำสมาชิกของลิสต์ new_list_1, new_list_2, new_list_3, และ new_list_4 มาต่อท้ายลิสต์ my_list เพื่อให้ได้ผลลัพธ์ดังนี้

[10, 11, 12, 4, 5, 6, 7, 8, 9, 1, 2, 3]

my_list = []
new_list_1 = [1, 2, 3]
new_list_2 = [4, 5, 6]
new_list_3 = [7, 8, 9]
new_list_4 = [10, 11, 12]

# ต่อลิสต์ my_list ด้วยคำสั่ง extend
my_list.extend(new_list_4)
my_list.extend(new_list_2)
my_list.extend(new_list_3)
my_list.extend(new_list_1)

print(my_list)
[10, 11, 12, 4, 5, 6, 7, 8, 9, 1, 2, 3]

การลบสมาชิกในลิสต์ด้วยคำสั่ง del

my_numbers = [11, 12, 13, 14, 15]
del my_numbers[2]
print(my_numbers)
[11, 12, 14, 15]

แบบฝึกหัด 9.6

จงลบสมาชิกออกจากลิสต์ my_numbers ตามรายการดังต่อไปนี้

  1. index ที่ 1
  2. index ที่ 2
  3. index ที่ 3

แล้วจึงแสดงลิสต์ my_numbers ออกที่หน้าจอ

my_numbers = [20, 50, 40, 70, 60, 90, 80, 100]

# ลบสมาชิกที่ index 1
del my_numbers[1]

# ลบสมาชิกที่ index 2
del my_numbers[2]

# ลบสมาชิกที่ index 3
del my_numbers[3]

print(my_numbers)
[20, 40, 60, 80, 100]
my_numbers = [11, 12, 13, 14, 15]
del my_numbers[1:4]
print(my_numbers)
[11, 15]

แบบฝึกหัด 9.7

จงลบสมาชิกในช่วง index ที่กำหนดออกจากลิสต์ my_numbers

  1. index ที่ 5 ถึง 10
  2. index ที่ 15 ถึง 20
# สร้างลิสต์จากช่วงจำนวน 0 ถึง 39
my_numbers = list(range(40))

# ลบสมาชิก index ที่ 5 ถึง 10
del my_numbers[5:10]

# ลบสมาชิก index ที่ 15 ถึง 20
del my_numbers[15:20]

print(my_numbers)
[0, 1, 2, 3, 4, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39]

การเรียงลำดับสมาชิกในลิสต์

items = [3, 8, 5, 10, 2, 9, 7, 6, 3, 8, 1, 4]
items.sort()
print(items)
[1, 2, 3, 3, 4, 5, 6, 7, 8, 8, 9, 10]
old_items = [3, 8, 5, 10, 2, 9, 7, 6, 3, 8, 1, 4]
new_items = sorted(old_items)
print('Old items:', old_items)
print('New items:', new_items)
Old items: [3, 8, 5, 10, 2, 9, 7, 6, 3, 8, 1, 4]
New items: [1, 2, 3, 3, 4, 5, 6, 7, 8, 8, 9, 10]
items = [3, 8, 5, 10, 2, 9, 7, 6, 3, 8, 1, 4]
items.sort(reverse=True)
print(items)
[10, 9, 8, 8, 7, 6, 5, 4, 3, 3, 2, 1]
old_items = [3, 8, 5, 10, 2, 9, 7, 6, 3, 8, 1, 4]
new_items = sorted(old_items, reverse=True)
print('Old items:', old_items)
print('New items:', new_items)
Old items: [3, 8, 5, 10, 2, 9, 7, 6, 3, 8, 1, 4]
New items: [10, 9, 8, 8, 7, 6, 5, 4, 3, 3, 2, 1]
items = [3, 8, 5, 10, 2, 9, 7, 6, 3, 8, 1, 4]
print('The item list is:', items)
print()

print('Sort and print')
for item in sorted(items):
    print('Item =', item)
print()

print('Reversed sort and print')
for item in sorted(items, reverse=True):
    print('Item =', item)
print()

print('The item list is still the same:', items)
The item list is: [3, 8, 5, 10, 2, 9, 7, 6, 3, 8, 1, 4]

Sort and print
Item = 1
Item = 2
Item = 3
Item = 3
Item = 4
Item = 5
Item = 6
Item = 7
Item = 8
Item = 8
Item = 9
Item = 10

Reversed sort and print
Item = 10
Item = 9
Item = 8
Item = 8
Item = 7
Item = 6
Item = 5
Item = 4
Item = 3
Item = 3
Item = 2
Item = 1

The item list is still the same: [3, 8, 5, 10, 2, 9, 7, 6, 3, 8, 1, 4]

แบบฝึกหัด 9.8

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

student_heights = [163.5, 150.0, 167.0, 161.25, 170.0]

print([i * 0.01 for i in sorted(student_heights, reverse=True)])
[1.7, 1.67, 1.635, 1.6125, 1.5]

การตรวจสอบการเป็นสมาชิก

my_list = ['hello', 'my', 'name', 'is', 'prachya']
if 'name' in my_list:
    print('It is in the list!')
else:
    print('It is not in the list.')
It is in the list!
my_list = ['hello', 'my', 'name', 'is', 'prachya']
if 'doraemon' not in my_list:
    print('It is not in the list!')
else:
    print('It is in the list.')
It is not in the list!

แบบฝึกหัด 9.9

จงนำสมาชิกจากลิสต์ my_list มาสร้างลิสต์ใหม่ชื่อว่า new_list โดยลบสมาชิกที่มีซ้ำกันออกไป

my_list = [3, 8, 5, 10, 2, 9, 7, 6, 3, 8, 1, 4]
new_list = []

for element in my_list:
    if element not in new_list:
        new_list.append(element)

print(new_list)
[3, 8, 5, 10, 2, 9, 7, 6, 1, 4]

ทูเพิล (tuple)

# คู่ลำดับเป็น tuple ที่มีสมาชิก 2 ตัว
coor1 = (3, 5)
coor2 = (4, 7)
print(coor1)
print(coor2)

# คู่ลำดับสามารถมีสมาชิกได้หลายตัว
my_tuple = (3, 8, 5, 10, 2, 9, 7, 6, 3, 8, 1, 4)
print(my_tuple)
(3, 5)
(4, 7)
(3, 8, 5, 10, 2, 9, 7, 6, 3, 8, 1, 4)
len(my_tuple)
12
coor_2d = (3, 5)
coor_3d = (1, 2, 3)

x1, y1 = coor_2d
x2, y2, z2 = coor_3d

print(x1, y1)
print(x2, y2, z2)
3 5
1 2 3
my_tuple[2:7]
(5, 10, 2, 9, 7)
(1, 2, 3) < (1, 2, 4)        # ทูเพิลแรกน้อยกว่าทูเพิลหลัง เพราะเปรียบเทียบทีละคู่จนไปเจอว่า 3 < 4
True
(1, 2, 3) < (1, 2, 3, 0)     # ทูเพิลแรกน้อยกว่าทูเพิลหลัง เพราะทูเพิลแรกหมดสมาชิกก่อน
True
student_scores = [
    ('John', 35.5), ('Mary', 73.0), ('Cindy', 83.5), ('Sean', 65.0),
    ('Barry', 81.0), ('Mark', 79.5), ('Eugene', 87.0), ('Bob', 49.5)
]

for (name, score) in student_scores:
    print(name, ':', score)
John : 35.5
Mary : 73.0
Cindy : 83.5
Sean : 65.0
Barry : 81.0
Mark : 79.5
Eugene : 87.0
Bob : 49.5
student_scores = [
    ('John', 35.5), ('Mary', 73.0), ('Cindy', 83.5), ('Sean', 65.0),
    ('Barry', 81.0), ('Mark', 79.5), ('Eugene', 87.0), ('Bob', 49.5)
]

student_scores.append( ('Peter', 90.0) )     # โปรดสังเกตว่าจะมีวงเล็บครอบทูเพิลด้วย

แบบฝึกหัด 9.10

  1. จงแสดงชื่อและคะแนนของนักเรียนจากในลิสต์ student_scores ซึ่งได้คะแนนสอบมากกว่า 70 คะแนน
student_scores = [
    ('John', 35.5), ('Mary', 73.0), ('Cindy', 83.5), ('Sean', 65.0),
    ('Barry', 81.0), ('Mark', 79.5), ('Eugene', 87.0), ('Bob', 49.5)
]

for (name, score) in student_scores:
    if score > 70:
        print(name)
Mary
Cindy
Barry
Mark
Eugene
  1. จงแสดงชื่อและคะแนนของนักเรียนในลิสต์ student_scores โดยเรียงลำดับตามคะแนนจากมากไปหาน้อย
student_scores = [
    ('John', 35.5), ('Mary', 73.0), ('Cindy', 83.5), ('Sean', 65.0),
    ('Barry', 81.0), ('Mark', 79.5), ('Eugene', 87.0), ('Bob', 49.5)
]

inv_scores = []

# กลับลำดับของทูเพิลในลิสต์ student_scores ให้กลายเป็น (คะแนน, ชื่อ) แล้วเก็บลงใน inv_scores
for (nm,sc) in student_scores:          # แก้โค้ดที่นี่
    inv_scores.append((sc,nm))        # แก้โค้ดที่นี่

# เรียงลำดับข้อมูลใน inv_scores ด้วยคำสั่ง sort โดยเรียงจากมากไปน้อย
inv_scores.sort(reverse=True)        # แก้โค้ดที่นี่

# แสดงชื่อและคะแนนของนักเรียนในลิสต์ inv_scores ออกมา
for (score, name) in inv_scores:
    print(name, ':', score)
Eugene : 87.0
Cindy : 83.5
Barry : 81.0
Mark : 79.5
Mary : 73.0
Sean : 65.0
Bob : 49.5
John : 35.5

กิจกรรม 10: การวนซ้ำแบบกำหนดเงื่อนไข

ลูปไวล์ (while-loop)

items = [3, 8, 5, 10, 2, 9, 7, 6, 3, 8, 1, 4]
idx = 0
jumping_sum = 0

while idx < len(items) and items[idx] != 7:
    print('Adding', items[idx])
    jumping_sum = jumping_sum + items[idx]
    idx = idx + 2
    
print('The jumping sum is', jumping_sum)
Adding 3
Adding 5
Adding 2
The jumping sum is 10
items = [3, 8, 5, 10, 2, 9, 7, 6, 3, 8, 1, 4]
idx = 0
my_sum = 0

while idx < len(items):
    print('Adding', items[idx])
    my_sum = my_sum + items[idx]
    
    # สังเกตว่าคำสั่ง break จะทำงานหลังจากที่ได้บวกผลรวมเรียบร้อยแล้ว
    if items[idx] <= 2:
        break
        
    idx = idx + 1
    
print(my_sum)
Adding 3
Adding 8
Adding 5
Adding 10
Adding 2
28

แบบฝึกหัด 10.1

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

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

my_numbers = []

# วนลูปตลอดไปจนกว่าจะถูกสั่ง break
while True:
    num = int(input('Enter an integer: '))
    
    # หากค่า num เป็น 0 ให้ออกจากลูปด้วยคำสั่ง break
    if num == 0:
        break
        
    # นำค่า num ไปต่อท้ายลิสต์ my_numbers
    my_numbers.append(num)

# เรียงลำดับสมาชิกในลิสต์ my_numbers จากมากไปน้อยด้วยคำสั่ง sort
my_numbers.sort()
    
print(my_numbers)
[1, 2, 5, 5, 5, 5]

กิจกรรม 11: เซ็ต (set)

my_list = [300, 200, 100, 300, 200]
my_set = {300, 200, 100, 300, 200}

print('This is a list:', my_list)
print('This is a set:', my_set)
This is a list: [300, 200, 100, 300, 200]
This is a set: {200, 100, 300}
for member in my_set:
    print(member)
200
100
300
print('200 in my_set is', 200 in my_set)
print('400 in my_set is', 400 in my_set)
200 in my_set is True
400 in my_set is False
my_empty_set = set()
print(my_empty_set)
set()

การเพิ่มสมาชิกในเซ็ตด้วยคำสั่ง add และ update

my_set = {300, 200, 100}
print('my_set =', my_set, end='\n\n')

print('Adding 400')
my_set.add(400)
print('my_set =', my_set, end='\n\n')

print('Adding 200')
my_set.add(200)
print('my_set =', my_set, end='\n\n')

print('Updating with [400, 500, 600]')
my_set.update([300, 400, 500, 600])
print('my_set =', my_set)
my_set = {200, 100, 300}

Adding 400
my_set = {200, 100, 300, 400}

Adding 200
my_set = {200, 100, 300, 400}

Updating with [400, 500, 600]
my_set = {100, 200, 300, 400, 500, 600}

แบบฝึกหัด 11.1

จงหาเซ็ตของชื่อนักเรียนจากในรายการคะแนนนักเรียนดังต่อไปนี้

student_scores = [
    ('John', 35.5), ('Mary', 73.0), ('Cindy', 83.5), ('Sean', 65.0),
    ('Barry', 81.0), ('Mark', 79.5), ('Eugene', 87.0), ('Bob', 49.5)
]

# สร้างตัวแปร students เพื่อเก็บเซ็ตว่าง
students = []                             # แก้โค้ดที่นี่

# ไล่ลูปฟอร์เพื่อเติมชื่อนักเรียนลงในเซ็ต students
for (name, score) in student_scores:      # แก้โค้ดที่นี่
    students.append(name)                 # แก้โค้ดที่นี่

print(students)
['John', 'Mary', 'Cindy', 'Sean', 'Barry', 'Mark', 'Eugene', 'Bob']

ตัวดำเนินการบนเซ็ต

ตัวดำเนินการ ภาษา Python ความหมาย
Union (∪) s1 ⏐ s2 นำสมาชิกทั้งสองเซ็ตมารวมกัน
Intersection (∩) s1 & s2 เลือกสมาชิกที่อยู่ในทั้งสองเซ็ต
Difference (-) s1 - s2 เลือกเฉพาะสมาชิกที่อยู่ในเซ็ตแรกแต่ไม่อยู่ในเซ็ตสอง
Symmetric difference (∆) s1 ^ s2 เลือกเฉพาะสมาชิกที่อยู่ในเซ็ตใดเซ็ตหนึ่งเท่านั้น
Membership (∈) e in s $e$ อยู่ในเซ็ต $s$ หรือไม่
Subset (⊆) s1 <= s2 เซ็ต s1 เป็นสับเซ็ตของ s2 หรือไม่
Superset (⊇) s1 >= s2 เซ็ต s1 เป็นซูเปอร์เซ็ตของ s2 หรือไม่
s1 = {1, 2, 3   }
s2 = {   2, 3, 4}
print('s1 =', s1)
print('s2 =', s2, end='\n\n')

print('Union:                    s1 ∪ s2 =', s1 | s2)
print('Intersection:             s1 ∩ s2 =', s1 & s2)
print('Difference:               s1 - s2 =', s1 - s2)
print('                          s2 - s1 =', s2 - s1)
print('Symmetric difference:     s1 ∆ s2 =', s1 ^ s2)
print('Membership:                3 ∈ s1 =', 3 in s1)
print('Subset:               {2, 3} ⊆ s1 =', {2, 3} <= s1)
print('                   {1, 2, 3} ⊆ s1 =', {1, 2, 3} <= s1)
print('Pure subset:          {2, 3} ⊂ s1 =', {2, 3} < s1)
print('                   {1, 2, 3} ⊂ s1 =', {1, 2, 3} < s1)
print('Superset:          s1 ⊇ {2, 3}    =', s1 >= {2, 3})
print('                   s1 ⊇ {1, 2, 3} =', s1 >= {1, 2, 3})
print('Pure superset:     s1 ⊃ {2, 3}    =', s1 > {2, 3})
print('                   s1 ⊃ {1, 2, 3} =', s1 > {1, 2, 3})
s1 = {1, 2, 3}
s2 = {2, 3, 4}

Union:                    s1 ∪ s2 = {1, 2, 3, 4}
Intersection:             s1 ∩ s2 = {2, 3}
Difference:               s1 - s2 = {1}
                          s2 - s1 = {4}
Symmetric difference:     s1 ∆ s2 = {1, 4}
Membership:                3 ∈ s1 = True
Subset:               {2, 3} ⊆ s1 = True
                   {1, 2, 3} ⊆ s1 = True
Pure subset:          {2, 3} ⊂ s1 = True
                   {1, 2, 3} ⊂ s1 = False
Superset:          s1 ⊇ {2, 3}    = True
                   s1 ⊇ {1, 2, 3} = True
Pure superset:     s1 ⊃ {2, 3}    = True
                   s1 ⊃ {1, 2, 3} = False

แบบฝึกหัด 11.2

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

หลังจากนั้นให้คำนวณเซ็ตดังต่อไปนี้

  1. นักเรียนที่เป็นทั้งยอดเยี่ยม**และ**เรียนดีในเวลาเดียวกัน คือจะเป็นเฉพาะยอดเยี่ยมหรือเรียนดีอย่างเดียวไม่ได้

  2. นักเรียนที่เป็นยอดเยี่ยม**เท่านั้น** ไม่ได้เป็นเรียนดี

  3. นักเรียนที่เป็นเรียนดี**เท่านั้น** ไม่ได้เป็นยอดเยี่ยม

  4. นักเรียนที่ได้เป็นยอดเยี่ยมหรือได้เป็นเรียนดี **อย่างใดอย่างหนึ่งเท่านั้น**

student_scores = [
    ('John', 35.5), ('Mary', 73.0), ('Cindy', 83.5), ('Sean', 65.0),
    ('Barry', 81.0), ('Mark', 79.5), ('Eugene', 87.0), ('Bob', 49.5)
]

# เซ็ตของชื่อนักเรียนยอดเยี่ยม
excellent_students = set()

# เซ็ตของชื่อนักเรียนเรียนดี
good_students = set()

for (name, score) in student_scores:
    
    # กรณีนักเรียนยอดเยี่ยม ให้นำเฉพาะชื่อนักเรียนใส่เซ็ต excellent_students ไว้
    if score >= 80:
        excellent_students.add(name)
        
    # กรณีนักเรียนเรียนดี ให้นำเฉพาะชื่อนักเรียนใส่เซ็ต good_students ไว้
    if score >= 70 and score < 85:
        good_students.add(name)
        
print('นักเรียนยอดเยี่ยม:', excellent_students)
print('นักเรียนเรียนดี:', good_students, end='\n\n')

print('นักเรียนยอดเยี่ยมและเรียนดี:', excellent_students & good_students)
print('นักเรียนยอดเยี่ยมแต่ไม่ได้เรียนดี:', excellent_students - good_students)
print('นักเรียนเรียนดีแต่ไม่ได้ยอดเยี่ยม:', good_students - excellent_students)
print('นักเรียนเรียนดีหรือยอดเยี่ยม อย่างใดอย่างหนึ่ง:', excellent_students ^ good_students)
นักเรียนยอดเยี่ยม: {'Cindy', 'Eugene', 'Barry'}
นักเรียนเรียนดี: {'Mark', 'Cindy', 'Mary', 'Barry'}

นักเรียนยอดเยี่ยมและเรียนดี: {'Cindy', 'Barry'}
นักเรียนยอดเยี่ยมแต่ไม่ได้เรียนดี: {'Eugene'}
นักเรียนเรียนดีแต่ไม่ได้ยอดเยี่ยม: {'Mark', 'Mary'}
นักเรียนเรียนดีหรือยอดเยี่ยม อย่างใดอย่างหนึ่ง: {'Mary', 'Eugene', 'Mark'}

กิจกรรม 12: ดิกชันนารี (dictionary)

การสร้างดิกชันนารี

# ดิกชันนารีเปล่า
my_empty_dict = {}

# ดิกชันนารีที่มีค่าเริ่มต้น
student_dict = {
    'John': 35.5, 'Mary': 73.0, 'Cindy': 83.5, 'Sean': 65.0,
    'Barry': 81.0, 'Mark': 79.5, 'Eugene': 87.0, 'Bob': 49.5
}

print(my_empty_dict)

print(student_dict)
{}
{'John': 35.5, 'Mary': 73.0, 'Cindy': 83.5, 'Sean': 65.0, 'Barry': 81.0, 'Mark': 79.5, 'Eugene': 87.0, 'Bob': 49.5}

แบบฝึกหัด 12.1

จงสร้างดิกชันนารี phone_dir ซึ่งมีค่าเริ่มต้นดังต่อไปนี้

phone_dir = {
  "Prachya": '533-7855',
  "Methas": '536-2313',
  "Pahol": '524-0935'
}

แบบฝึกหัด 12.2

จงเขียนโปรแกรมรับชื่อและเบอร์โทรศัพท์จากผู้ใช้ หลังจากนั้นให้สร้างดิกชันนารี phone_entry ที่มีโครงสร้างดังนี้

หลังจากนั้นจึงแสดงดิกชันนารีนี้ออกทางหน้าจอ

first_name = input('Enter first name: ')
last_name = input('Enter last name: ')
tel_no = input('Enter tel no: ')

phone_entry = { 'first_name': first_name, 'last_name': last_name, 'tel_no': last_name }       # แก้ไขโค้ดที่นี่

print(phone_entry)
{'first_name': 'Jane', 'last_name': 'Smith', 'tel_no': 'Smith'}

แบบฝึกหัด 12.3

จงเขียนโปรแกรมทยอยรับชื่อและเบอร์โทรศัพท์จากผู้ใช้ สร้างดิกชันนารี phone_entry ที่มีโครงสร้างดังนี้

แล้วนำดิกชันนารีนี้ไปต่อท้ายในลิสต์ phone_database ซึ่งเก็บฐานข้อมูลของผู้ใช้โทรศัพท์

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

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

# ลิสต์สำหรับฐานข้อมูลผู้ใช้โทรศัพท์
phone_database = []

# วนลูปตลอดไป
while True:
    
    # รับชื่อ นามสกุล และเบอร์โทรศัพท์จากผู้ใช้
    first_name = input('Enter first name: ')
    last_name = input('Enter last name: ')
    tel_no = input('Enter tel no: ')
    
    # ถ้าหากชื่อ นามสกุล หรือเบอร์โทรศัพท์เป็นสตริงว่าง ให้หยุดการทำงานของลูปนี้ด้วย break
    if first_name == '' or last_name == '':
        break

    # นำชื่อ นามสกุล และเบอร์โทรศัพท์ มาสร้างดิกชันนารี
    phone_entry = { 'fn': first_name, 'ln': last_name }       # แก้ไขโค้ดที่นี่
    
    # นำดิกชันนารีนี้ไปต่อท้าย phone_database
    phone_database.append(phone_entry)       # แก้ไขโค้ดที่นี่

# แสดงผลดิกชันนารีใน phone_database ทีละอัน
for entry in phone_database:
    print(f"Name: {entry['fn']} {entry['ln']}")       # แก้ไขโค้ดที่นี่
Name: John Smith

การเข้าถึงค่าในคีย์

student_dict = {
    'John': 35.5, 'Mary': 73.0, 'Cindy': 83.5, 'Sean': 65.0,
    'Barry': 81.0, 'Mark': 79.5, 'Eugene': 87.0, 'Bob': 49.5
}

print(student_dict['Mary'])
print(student_dict['Eugene'])
73.0
87.0
print(student_dict['Lucy'])
---------------------------------------------------------------------------

KeyError                                  Traceback (most recent call last)

/tmp/ipykernel_4316/1927303255.py in <module>
----> 1 print(student_dict['Lucy'])


KeyError: 'Lucy'
print('Lucy' in student_dict)
print('Mary' in student_dict)
False
True

การเพิ่มและแทนที่คีย์ในดิกชันนารี

student_dict = {
    'John': 35.5, 'Mary': 73.0, 'Cindy': 83.5, 'Sean': 65.0,
    'Barry': 81.0, 'Mark': 79.5, 'Eugene': 87.0, 'Bob': 49.5
}

student_dict['Rupert'] = 91.0

print(student_dict)
{'John': 35.5, 'Mary': 73.0, 'Cindy': 83.5, 'Sean': 65.0, 'Barry': 81.0, 'Mark': 79.5, 'Eugene': 87.0, 'Bob': 49.5, 'Rupert': 91.0}
student_dict = {
    'John': 35.5, 'Mary': 73.0, 'Cindy': 83.5, 'Sean': 65.0,
    'Barry': 81.0, 'Mark': 79.5, 'Eugene': 87.0, 'Bob': 49.5
}

student_dict['Mary'] = 89.0

student_dict['Barry'] = student_dict['Barry'] + 5

print(student_dict)
{'John': 35.5, 'Mary': 89.0, 'Cindy': 83.5, 'Sean': 65.0, 'Barry': 86.0, 'Mark': 79.5, 'Eugene': 87.0, 'Bob': 49.5}

แบบฝึกหัด 12.4

จงเขียนโปรแกรมเพื่อเก็บคะแนนสอบของนักเรียนลงในดิกชันนารี student_dict โดยโปรแกรมจะถามหาชื่อของนักเรียน

  1. หากชื่อนั้นมีอยู่ใน student_dict อยู่แล้ว ก็ให้แสดงคะแนนที่เคยกรอกไว้แล้วออกมา

  2. ไม่เช่นนั้นก็ให้คอมพิวเตอร์ถามคะแนนจากผู้ใช้เป็นจำนวนจริง (แปลงด้วยคำสั่ง float) แล้วเก็บลงในดิกชันนารีดังกล่าว

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

สุดท้ายโปรแกรมจะแสดงดิกชันนารี student_dict ออกทางหน้าจอ

# ดิกชันนารีเก็บคะแนนสอบของนักเรียน
student_dict = {
    'John': 35.5, 'Mary': 73.0, 'Cindy': 83.5, 'Sean': 65.0,
    'Barry': 81.0, 'Mark': 79.5, 'Eugene': 87.0, 'Bob': 49.5
}


# วนลูปตลอดไป
while True:
    
    # รับชื่อนักเรียนจากผู้ใช้
    name = input('Enter student name: ')
    
    # ถ้าชื่อเป็นสตริงว่าง ให้หยุดการทำงานด้วยคำสั่ง break
    if name == '':
        break       # แก้ไขโค้ดที่นี่
       
    # ถ้าหากชื่อของนักเรียนมีอยู่ใน student_dict อยู่แล้ว
    if name in student_dict:
        # ให้แสดงคะแนนของนักเรียนคนนั้นออกทางหน้าจอ
        print('Score =', student_dict[name] )       # แก้ไขโค้ดที่นี่
        
    # ไม่เช่นนั้น
    else:
        # รับค่าคะแนนจากผู้ใช้
        score = float(input('Enter score: '))
        # บันทึกคะแนนของนักเรียน โดยใช้ name เป็นคีย์ในการบันทึกลงในดิกชันนารี
        student_dict[name] = score       # แก้ไขโค้ดที่นี่
        
    # print()
        
print(student_dict)
Score = 73.0
Score = 79.5
{'John': 35.5, 'Mary': 73.0, 'Cindy': 83.5, 'Sean': 65.0, 'Barry': 81.0, 'Mark': 79.5, 'Eugene': 87.0, 'Bob': 49.5}

การลบคีย์ในดิกชันนารี

student_dict = {
    'John': 35.5, 'Mary': 73.0, 'Cindy': 83.5, 'Sean': 65.0,
    'Barry': 81.0, 'Mark': 79.5, 'Eugene': 87.0, 'Bob': 49.5
}

del student_dict['Barry']
print(student_dict)
{'John': 35.5, 'Mary': 73.0, 'Cindy': 83.5, 'Sean': 65.0, 'Mark': 79.5, 'Eugene': 87.0, 'Bob': 49.5}

การวนลูปกับดิกชันนารี

student_dict = {
    'John': 35.5, 'Mary': 73.0, 'Cindy': 83.5, 'Sean': 65.0,
    'Barry': 81.0, 'Mark': 79.5, 'Eugene': 87.0, 'Bob': 49.5
}

for name in student_dict:
    print(name, ':', student_dict[name])
John : 35.5
Mary : 73.0
Cindy : 83.5
Sean : 65.0
Barry : 81.0
Mark : 79.5
Eugene : 87.0
Bob : 49.5
student_dict = {
    'John': 35.5, 'Mary': 73.0, 'Cindy': 83.5, 'Sean': 65.0,
    'Barry': 81.0, 'Mark': 79.5, 'Eugene': 87.0, 'Bob': 49.5
}

for name in sorted(student_dict):
    print(name, ':', student_dict[name])
Barry : 81.0
Bob : 49.5
Cindy : 83.5
Eugene : 87.0
John : 35.5
Mark : 79.5
Mary : 73.0
Sean : 65.0
student_dict = {
    'John': 35.5, 'Mary': 73.0, 'Cindy': 83.5, 'Sean': 65.0,
    'Barry': 81.0, 'Mark': 79.5, 'Eugene': 87.0, 'Bob': 49.5
}

for name, score in student_dict.items():
    print(name, ':', score)
John : 35.5
Mary : 73.0
Cindy : 83.5
Sean : 65.0
Barry : 81.0
Mark : 79.5
Eugene : 87.0
Bob : 49.5
student_dict = {
    'John': 35.5, 'Mary': 73.0, 'Cindy': 83.5, 'Sean': 65.0,
    'Barry': 81.0, 'Mark': 79.5, 'Eugene': 87.0, 'Bob': 49.5
}

for name in student_dict:
    del student_dict[name]
---------------------------------------------------------------------------

RuntimeError                              Traceback (most recent call last)

/tmp/ipykernel_4316/4054559271.py in <module>
      4 }
      5 
----> 6 for name in student_dict:
      7     del student_dict[name]


RuntimeError: dictionary changed size during iteration

แบบฝึกหัด 12.5

จงเขียนโปรแกรมเพื่อแจ้งว่า นักเรียนคนใดในดิกชันนารี student_dict อยู่ในภาวะน่าเป็นห่วง (probation) โดยจะมีคะแนนน้อยกว่า 50 คะแนน และให้โปรแกรมเรียงลำดับชื่อตามตัวอักษรด้วย

# ดิกชันนารีเก็บคะแนนสอบของนักเรียน
student_dict = {
    'John': 35.5, 'Mary': 73.0, 'Cindy': 83.5, 'Sean': 65.0,
    'Barry': 81.0, 'Mark': 79.5, 'Eugene': 87.0, 'Bob': 49.5
}

# ใช้ลูปฟอร์ไล่คีย์และค่าในคีย์ โดยเรียงลำดับชื่อตามตัวอักษร
for name in sorted(student_dict):
    # ถ้าหากคะแนนต่ำกว่า 50 คะแนน แสดงว่าอยู่ในภาวะน่าเป็นห่วง
    if student_dict[name] < 50:
        print( name , 'is on probation.')
Bob is on probation.
John is on probation.

แบบฝึกหัด 12.6

จงเขียนโปรแกรมให้รับชื่อของนักเรียน หากชื่อของนักเรียนปรากฏอยู่ในดิกชันนารี student_dict ให้นับจำนวนมาว่า มีนักเรียนกี่คนที่มีคะแนนน้อยกว่านักเรียนคนนี้ แต่ถ้าหากไม่มี ให้ตอบว่ามี 0 คน

# ดิกชันนารีเก็บคะแนนสอบของนักเรียน
student_dict = {
    'John': 35.5, 'Mary': 73.0, 'Cindy': 83.5, 'Sean': 65.0,
    'Barry': 81.0, 'Mark': 79.5, 'Eugene': 87.0, 'Bob': 49.5
}

# ชื่อนักเรียน
name = input('Enter student name: ')

# ตัวนับจำนวนนักเรียนที่มีคะแนนน้อยกว่า
count = 0

# หากมีชื่อนักเรียนคนนี้ในดิกชันนารี student_dict
if name in student_dict:
    # ให้วนลูปฟอร์นักเรียนที่มีใน student_dict
    for index in student_dict:              # แก้ไขโค้ดที่นี่
        # ถ้าหากคะแนนต่ำกว่าคะแนนของนักเรียนที่ผู้ใช้ระบุ
        if student_dict[index] < student_dict[name]:           # แก้ไขโค้ดที่นี่
            count = count + 1
            
print('Number of students with lower score =', count)
Number of students with lower score = 0

กิจกรรม 13: การจัดฟอร์แมตของสตริง

name = 'Prachya'
telno = '533-7855'
age = 37
degree = 'high-school'

print(f'My name is {name} and I am {age} years old. I am still a {degree} student and my phone number is {telno}.')
My name is Prachya and I am 37 years old. I am still a high-school student and my phone number is 533-7855.

การกำหนดความกว้างของสตริง

user_database = [
    ('Prachya', '533-7855', 37, 'high-school'),
    ('Methas', '536-2313', 36, 'university'),
    ('Pahol', '524-0935', 38, 'university')
]

print(60 * '-')
for name, telno, age, degree in user_database:
    print(f'| {name:20} | {telno:15} | {degree:15} |')
print(60 * '-')
------------------------------------------------------------
| Prachya              | 533-7855        | high-school     |
| Methas               | 536-2313        | university      |
| Pahol                | 524-0935        | university      |
------------------------------------------------------------

การจัดสตริงไปทางซ้าย กลาง หรือขวา

print(60 * '-')
for name, telno, age, degree in user_database:
    print(f'| {name:<15} | {telno:^15} | {degree:>15} |')
print(60 * '-')
------------------------------------------------------------
| Prachya         |    533-7855     |     high-school |
| Methas          |    536-2313     |      university |
| Pahol           |    524-0935     |      university |
------------------------------------------------------------

แบบฝึกหัด 13.1

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

  1. นักเรียนคนที่ 1 จะมีความกว้าง 10 ตัวอักษร ชิดขวา
  2. นักเรียนคนที่ 2 จะมีความกว้าง 10 ตัวอักษรเหมือนกัน แต่ชิดซ้าย
  3. นักเรียนที่เป็นผู้ชนะ จะมีความกว้าง 10 ตัวอักษรเหมือนกัน แต่ว่าจัดกลาง
  4. ในกรณีที่คะแนนเสมอกัน ให้พิมพ์ว่า --DUE-- แบบจัดกลาง ลงในช่องของชื่อผู้ชนะแทน
student_scores = [
    ('John', 35.5), ('Mary', 73.0), ('Cindy', 83.5),
    ('Barry', 81.0), ('Mark', 79.5), ('Eugene', 87.0), ('Rupert', 81.0)
]

# พิมพ์หัวตาราง
print(42 * '-')
print(f'| {"Score Battles":^25} | {"Winners":^10} |')
print(42 * '-')

for student1, score1 in student_scores:
    for student2, score2 in student_scores:
        if student1 != student2:
            
            # พิมพ์ชื่อของนักเรียนคนที่ 1 (student1) และนักเรียนคนที่ 2 (student2)
            print(f'| {student1:>10} vs. {student2:<10} ', end='')
            
            # หาผู้ชนะคะแนน แล้วพิมพ์ชื่อผู้ชนะออกมา
            if score1 > score2:
                print(f'| {student1:^10} |')
            elif score1 < score2:
                print(f'| {student2:^10} |')
            # กรณีที่คะแนนเสมอกัน
            else:
                print(f'| {"--DUE--":^10} |')
                
    print(42 * '-')
------------------------------------------
|       Score Battles       |  Winners   |
------------------------------------------
|       John vs. Mary       |    Mary    |
|       John vs. Cindy      |   Cindy    |
|       John vs. Barry      |   Barry    |
|       John vs. Mark       |    Mark    |
|       John vs. Eugene     |   Eugene   |
|       John vs. Rupert     |   Rupert   |
------------------------------------------
|       Mary vs. John       |    Mary    |
|       Mary vs. Cindy      |   Cindy    |
|       Mary vs. Barry      |   Barry    |
|       Mary vs. Mark       |    Mark    |
|       Mary vs. Eugene     |   Eugene   |
|       Mary vs. Rupert     |   Rupert   |
------------------------------------------
|      Cindy vs. John       |   Cindy    |
|      Cindy vs. Mary       |   Cindy    |
|      Cindy vs. Barry      |   Cindy    |
|      Cindy vs. Mark       |   Cindy    |
|      Cindy vs. Eugene     |   Eugene   |
|      Cindy vs. Rupert     |   Cindy    |
------------------------------------------
|      Barry vs. John       |   Barry    |
|      Barry vs. Mary       |   Barry    |
|      Barry vs. Cindy      |   Cindy    |
|      Barry vs. Mark       |   Barry    |
|      Barry vs. Eugene     |   Eugene   |
|      Barry vs. Rupert     |  --DUE--   |
------------------------------------------
|       Mark vs. John       |    Mark    |
|       Mark vs. Mary       |    Mark    |
|       Mark vs. Cindy      |   Cindy    |
|       Mark vs. Barry      |   Barry    |
|       Mark vs. Eugene     |   Eugene   |
|       Mark vs. Rupert     |   Rupert   |
------------------------------------------
|     Eugene vs. John       |   Eugene   |
|     Eugene vs. Mary       |   Eugene   |
|     Eugene vs. Cindy      |   Eugene   |
|     Eugene vs. Barry      |   Eugene   |
|     Eugene vs. Mark       |   Eugene   |
|     Eugene vs. Rupert     |   Eugene   |
------------------------------------------
|     Rupert vs. John       |   Rupert   |
|     Rupert vs. Mary       |   Rupert   |
|     Rupert vs. Cindy      |   Cindy    |
|     Rupert vs. Barry      |  --DUE--   |
|     Rupert vs. Mark       |   Rupert   |
|     Rupert vs. Eugene     |   Eugene   |
------------------------------------------

การเข้าถึงดิกชันนารีและลิสต์

user_database = [
    {'name': 'Prachya', 'telno': '533-7855', 'age': 37, 'degree': 'high-school'},
    {'name': 'Methas', 'telno': '536-2313', 'age': 36, 'degree': 'university'},
    {'name': 'Pahol', 'telno': '524-0935', 'age': 38, 'degree': 'university'}
]

print(60 * '-')
for user in user_database:
    print(f'| {user["name"]:<20} | {user["telno"]:^15} | {user["degree"]:>15} |')
print(60 * '-')
------------------------------------------------------------
| Prachya              |    533-7855     |     high-school |
| Methas               |    536-2313     |      university |
| Pahol                |    524-0935     |      university |
------------------------------------------------------------

การจัดฟอร์แมตให้ตัวเลข

user_database = [
    {'name': 'Prachya', 'telno': '533-7855', 'age': 37, 'degree': 'high-school'},
    {'name': 'Methas', 'telno': '536-2313', 'age': 36, 'degree': 'university'},
    {'name': 'Pahol', 'telno': '524-0935', 'age': 38, 'degree': 'university'}
]

print(66 * '-')
for user in user_database:
    print(f'| {user["name"]:<20} | {user["telno"]:^15} | {user["age"]:>3d} | {user["degree"]:>15} |')
print(66 * '-')
------------------------------------------------------------------
| Prachya              |    533-7855     |  37 |     high-school |
| Methas               |    536-2313     |  36 |      university |
| Pahol                |    524-0935     |  38 |      university |
------------------------------------------------------------------
student_dict = {
    'John': 35.5, 'Mary': 73.0, 'Cindy': 83.5, 'Sean': 65.0,
    'Barry': 81.0, 'Mark': 79.5, 'Eugene': 87.0, 'Bob': 49.5
}

print(35 * '-')
for name, score in student_dict.items():
    print(f'| {name:<20} | {score:>8.2f} |')
print(35 * '-')
-----------------------------------
| John                 |    35.50 |
| Mary                 |    73.00 |
| Cindy                |    83.50 |
| Sean                 |    65.00 |
| Barry                |    81.00 |
| Mark                 |    79.50 |
| Eugene               |    87.00 |
| Bob                  |    49.50 |
-----------------------------------

แบบฝึกหัด 13.2

ก็อปปี้บางส่วนของโปรแกรมในแบบฝึกหัด 13.1 มาแก้ไข โดยเพิ่มอีก 1 คอลัมน์เข้าไปในตาราง

student_scores = [
    ('John', 35.5), ('Mary', 73.0), ('Cindy', 83.5),
    ('Barry', 81.0), ('Mark', 79.5), ('Eugene', 87.0), ('Rupert', 81.0)
]

# พิมพ์หัวตาราง
print(53 * '-')
print(f'| {"Score Battles":^25} | {"Winners":^10} | {"Diff":>8} |')
print(53 * '-')

for student1, score1 in student_scores:
    for student2, score2 in student_scores:
        if student1 != student2:
            
            # พิมพ์ชื่อของนักเรียนคนที่ 1 (student1) และนักเรียนคนที่ 2 (student2)
            print(f'| {student1:>10} vs. {student2:<10} ', end='')
            
            # หาผู้ชนะคะแนน แล้วพิมพ์ชื่อผู้ชนะออกมา
            if score1 > score2:
                score_diff = score1 - score2
                print(f'| {student1:^10} ', end='')
            elif score1 < score2:
                score_diff = score2 - score1
                print(f'| {student2:^10} ', end='')
            # กรณีที่คะแนนเสมอกัน
            else:
                score_diff = 0
                print(f'| {"--DUE--":^10} ', end='')
                
            # พิมพ์ผลต่างของคะแนน score_diff หรือไม่ก็เว้นว่างหากเป็น 0
            if score_diff > 0.0:
                print(f'| {score_diff:8.2f} |')
            else:
                print('|          |')
                
    print(53 * '-')
-----------------------------------------------------
|       Score Battles       |  Winners   |     Diff |
-----------------------------------------------------
|       John vs. Mary       |    Mary    |    37.50 |
|       John vs. Cindy      |   Cindy    |    48.00 |
|       John vs. Barry      |   Barry    |    45.50 |
|       John vs. Mark       |    Mark    |    44.00 |
|       John vs. Eugene     |   Eugene   |    51.50 |
|       John vs. Rupert     |   Rupert   |    45.50 |
-----------------------------------------------------
|       Mary vs. John       |    Mary    |    37.50 |
|       Mary vs. Cindy      |   Cindy    |    10.50 |
|       Mary vs. Barry      |   Barry    |     8.00 |
|       Mary vs. Mark       |    Mark    |     6.50 |
|       Mary vs. Eugene     |   Eugene   |    14.00 |
|       Mary vs. Rupert     |   Rupert   |     8.00 |
-----------------------------------------------------
|      Cindy vs. John       |   Cindy    |    48.00 |
|      Cindy vs. Mary       |   Cindy    |    10.50 |
|      Cindy vs. Barry      |   Cindy    |     2.50 |
|      Cindy vs. Mark       |   Cindy    |     4.00 |
|      Cindy vs. Eugene     |   Eugene   |     3.50 |
|      Cindy vs. Rupert     |   Cindy    |     2.50 |
-----------------------------------------------------
|      Barry vs. John       |   Barry    |    45.50 |
|      Barry vs. Mary       |   Barry    |     8.00 |
|      Barry vs. Cindy      |   Cindy    |     2.50 |
|      Barry vs. Mark       |   Barry    |     1.50 |
|      Barry vs. Eugene     |   Eugene   |     6.00 |
|      Barry vs. Rupert     |  --DUE--   |          |
-----------------------------------------------------
|       Mark vs. John       |    Mark    |    44.00 |
|       Mark vs. Mary       |    Mark    |     6.50 |
|       Mark vs. Cindy      |   Cindy    |     4.00 |
|       Mark vs. Barry      |   Barry    |     1.50 |
|       Mark vs. Eugene     |   Eugene   |     7.50 |
|       Mark vs. Rupert     |   Rupert   |     1.50 |
-----------------------------------------------------
|     Eugene vs. John       |   Eugene   |    51.50 |
|     Eugene vs. Mary       |   Eugene   |    14.00 |
|     Eugene vs. Cindy      |   Eugene   |     3.50 |
|     Eugene vs. Barry      |   Eugene   |     6.00 |
|     Eugene vs. Mark       |   Eugene   |     7.50 |
|     Eugene vs. Rupert     |   Eugene   |     6.00 |
-----------------------------------------------------
|     Rupert vs. John       |   Rupert   |    45.50 |
|     Rupert vs. Mary       |   Rupert   |     8.00 |
|     Rupert vs. Cindy      |   Cindy    |     2.50 |
|     Rupert vs. Barry      |  --DUE--   |          |
|     Rupert vs. Mark       |   Rupert   |     1.50 |
|     Rupert vs. Eugene     |   Eugene   |     6.00 |
-----------------------------------------------------

กิจกรรม 14: การอ่านและเขียนไฟล์

การเปิดไฟล์และปิดไฟล์

fhdl = open('test_file_1.txt', 'w')         # fhdl ย่อมาจากคำว่า file handler
fhdl.close()

การเขียนข้อมูลลงไฟล์

fhdl = open('test_file_1.txt', 'w')

for i in range(1, 11):
    fhdl.write(f'{10 * i}\n')

fhdl.close()

การอ่านข้อมูลทีละ 1 บรรทัดจากไฟล์

fhdl = open('test_file_1.txt', 'r')

for line in fhdl:
    print(line)

fhdl.close()
10

20

30

40

50

60

70

80

90

100
fhdl = open('test_file_1.txt', 'r')

my_sum = 0

for line in fhdl:
    numstr = line.strip()       # กำจัดเว้นวรรคและขึ้นบรรทัดใหม่ซ้ายขวาทิ้งไป
    my_sum = my_sum + int(numstr)

fhdl.close()

print(my_sum)
550

การอ่านไฟล์ทุกบรรทัดในคราวเดียว

fhdl = open('test_file_1.txt', 'r')
lines = fhdl.readlines()
fhdl.close()
print(lines)

for i in range(len(lines)):
    lines[i] = lines[i].strip()

print(lines)
['10\n', '20\n', '30\n', '40\n', '50\n', '60\n', '70\n', '80\n', '90\n', '100\n']
['10', '20', '30', '40', '50', '60', '70', '80', '90', '100']

แบบฝึกหัด 14.1

จงอ่านข้อมูลจากไฟล์ test_file_1.txt แล้วเขียนข้อมูลลงไปในไฟล์ test_file_2.txt โดยจะต้องกลับลำดับการเขียนด้วย ดังนั้นในไฟล์ test_file_2.txt จะต้องมีข้อมูลที่สลับลำดับบรรทัดกับไฟล์ test_file_1.txt ที่เป็นอินพุต

# เปิดไฟล์ test_file_1.txt แบบอ่านอย่างเดียว
ifhdl = open('test_file_1.txt', 'r')

# อ่านทุกบรรทัดของไฟล์ ifhdl ใส่ตัวแปร lines
lines = ifhdl.readlines()

# ปิดไฟล์ทันทีที่ใช้เสร็จ
ifhdl.close()


# กลับลำดับของสมาชิกในลิสต์ lines
lines.reverse()


# เปิดไฟล์ test_file_2.txt แบบเขียนทับอย่างเดียว
ofhdl = open('test_file_2.txt', 'w')

# วนลูปตามบรรทัด
for i in range(len(lines)):
    # เขียนบรรทัดดังกล่าวลงในไฟล์ ofhdl
    ofhdl.write(lines[i])

# ปิดไฟล์ทันทีที่ใช้เสร็จ
ofhdl.close()

การแตกสตริง (tokenization)

tokens = 'hello this is prachya and i am glad to be here'.split()
print(tokens)
['hello', 'this', 'is', 'prachya', 'and', 'i', 'am', 'glad', 'to', 'be', 'here']
tokens = '   hello    this is    prachya  and i    am glad    to be   here   '.split()
print(tokens)
['hello', 'this', 'is', 'prachya', 'and', 'i', 'am', 'glad', 'to', 'be', 'here']
tokens = '533-7855,536-2313,524-7306'.split(',')
print(tokens)
['533-7855', '536-2313', '524-7306']
tokens = '533-7855,,536-2313,,,,524-7306'.split(',')
print(tokens)
['533-7855', '', '536-2313', '', '', '', '524-7306']

การเชื่อมลิสต์ของสตริง

words = ['hello', 'this', 'is', 'prachya', 'and', 'i', 'am', 'glad', 'to', 'be', 'here']
print(' '.join(words))
hello this is prachya and i am glad to be here
words = ['hello', 'this', 'is', 'prachya', 'and', 'i', 'am', 'glad', 'to', 'be', 'here']
print(''.join(words))
hellothisisprachyaandiamgladtobehere

การกำจัดช่องว่างที่ด้านซ้ายและขวาของสตริง

text = '     In the beginning, there was darkness.      '
print(f'|{text}|')
print(f'|{text.strip()}|')
|     In the beginning, there was darkness.      |
|In the beginning, there was darkness.|
number_text = '   10   ,    20   ,    30  ,    40  ,    50    '
tokens = number_text.split(',')

my_sum = 0

for token in tokens:
    num = token.strip()
    my_sum = my_sum + int(num)
    
print(my_sum)
150

แบบฝึกหัด 14.2

จงรับตัวเลข 1 ตัวจากผู้ใช้ แล้วสร้างไฟล์ test_file_3.txt ดังตัวอย่างต่อไปนี้

Enter n: 5

ส่วนตรงนี้คือเนื้อหาของไฟล์ที่ได้จากค่าอินพุต n

50      40      30      20      10      
40      30      20      10      
30      20      10      
20      10      
10      

โดยตัวเลขทุกตัวจะต้องมีความกว้าง 10 ตัวอักษรชิดซ้าย

n = 5 or int(input('Enter n: '))

ofhdl = open('test_file_3.txt', 'w')

for i in range(n, 0, -1):
    for j in range(i, 0, -1):
        ofhdl.write(f'{j*10:<10}')
    ofhdl.write('\n')

ofhdl.close()

ท่านสามารถตรวจสอบผลลัพธ์ได้จากการรันโปรแกรมด้านล่างนี้

ifhdl = open('test_file_3.txt', 'r')
for line in ifhdl:
    print(line.strip())
ifhdl.close()
50        40        30        20        10
40        30        20        10
30        20        10
20        10
10

แบบฝึกหัด 14.3

จงอ่านไฟล์ test_file_3.txt จากแบบฝึกหัด 14.2 แล้วหาผลรวมของตัวเลขในแต่ละบรรทัด เช่น ถ้าไฟล์ test_file_3.txt มีเนื้อหาเป็นดังนี้

50      40      30      20      10      
40      30      20      10      
30      20      10      
20      10      
10      

เราจะได้ผลลัพธ์ดังต่อไปนี้

150
100
60
30
10
ifhdl = open('test_file_3.txt', 'r')

# วนลูปแต่ละบรรทัดในไฟล์ ifhdl
for line in ifhdl.readlines():
    
    # แตกบรรทัดออกเป็น token ด้วยคำสั่ง split
    tokens = line.strip().split()
    
    # ตัวแปรเก็บผลรวมรายบรรทัด
    my_sum = 0
    
    for token in tokens:
        
        # แปลงแต่ละคำให้กลายเป็นตัวเลขจำนวนเต็ม
        num = int(token)
        
        # คำนวณผลรวมของบรรทัดนั้น
        my_sum = my_sum + num
        
    print(my_sum)

ifhdl.close()
150
100
60
30
10

กิจกรรม 15: ฟังก์ชัน

การนิยามฟังก์ชัน

def print_student(name, age, height):
    print(f'Name   : {name}')
    print(f'Age    : {age:d} y/o')
    print(f'Height : {height:6.2f} cm')
    print()
student_scores = [
    ('John', 13, 165.5), ('Mary', 14, 173.0), ('Cindy', 12, 163.5)
]

for name, age, height in student_scores:
    print_student(name, age, height)
Name   : John
Age    : 13 y/o
Height : 165.50 cm

Name   : Mary
Age    : 14 y/o
Height : 173.00 cm

Name   : Cindy
Age    : 12 y/o
Height : 163.50 cm

แบบฝึกหัด 15.1: การรวบรวมเป็นหน่วยด้วยฟังก์ชัน

ในแบบฝึกหัดนี้ เราจะมาหัดแยกแยะพารามิเตอร์ออกจากกระบวนการ เพื่อมานิยามฟังก์ชันกันครับ

  1. ลองสังเกตโค้ดดังต่อไปนี้ แล้วแยกแยะพารามิเตอร์ออกมาจากโค้ดที่ซ้ำกันอยู่
house_transactions = [
    ('Salary', +60000.0),
    ('Food', -10000.0),
    ('House mortgage', -20000.0),
    ('Consultancy', +20000.0),
    ('Credit card', -10000.0)
]

patisserie_transactions = [
    ('Flour', -2000.0),
    ('Cake sales', +30000.0),
    ('Tea sales', +10000.0),
    ('Staff salary', -15000.0),
    ('Electricity', -3000.0)
]

print(57 * '-')
for house_item, house_income in house_transactions:
    if house_income > 0:
        print(f'| {house_item:25} | {house_income:>25.2f} |')
    else:
        print(f'| {house_item:25} | {house_income:<25.2f} |')
print(57 * '-')
print()

print(57 * '-')
for shop_item, shop_income in patisserie_transactions:
    if shop_income > 0:
        print(f'| {shop_item:25} | {shop_income:>25.2f} |')
    else:
        print(f'| {shop_item:25} | {shop_income:<25.2f} |')
print(57 * '-')
print()
---------------------------------------------------------
| Salary                    |                  60000.00 |
| Food                      | -10000.00                 |
| House mortgage            | -20000.00                 |
| Consultancy               |                  20000.00 |
| Credit card               | -10000.00                 |
---------------------------------------------------------

---------------------------------------------------------
| Flour                     | -2000.00                  |
| Cake sales                |                  30000.00 |
| Tea sales                 |                  10000.00 |
| Staff salary              | -15000.00                 |
| Electricity               | -3000.00                  |
---------------------------------------------------------
  1. แยกโค้ดที่ซ้ำกันออกมานิยามเป็นฟังก์ชันใหม่ ตั้งชื่อว่า print_transactions
def print_transactions(transactions):
    print(57 * '-')
    for item, income in transactions:
        if income > 0:
            print(f'| {item:25} | {income:>25.2f} |')
        else:
            print(f'| {item:25} | {income:<25.2f} |')
    print(57 * '-')
    print()
  1. ปรับปรุงโค้ดในข้อ 1 ให้เรียกใช้ฟังก์ชัน print_transactions แทน
house_transactions = [
    ('Salary', +60000.0),
    ('Food', -10000.0),
    ('House mortgage', -20000.0),
    ('Consultancy', +20000.0),
    ('Credit card', -10000.0)
]

patisserie_transactions = [
    ('Flour', -2000.0),
    ('Cake sales', +30000.0),
    ('Tea sales', +10000.0),
    ('Staff salary', -15000.0),
    ('Electricity', -3000.0)
]

print_transactions(house_transactions) # เติมโค้ดที่นี่
print_transactions(patisserie_transactions) # เติมโค้ดที่นี่
---------------------------------------------------------
| Salary                    |                  60000.00 |
| Food                      | -10000.00                 |
| House mortgage            | -20000.00                 |
| Consultancy               |                  20000.00 |
| Credit card               | -10000.00                 |
---------------------------------------------------------

---------------------------------------------------------
| Flour                     | -2000.00                  |
| Cake sales                |                  30000.00 |
| Tea sales                 |                  10000.00 |
| Staff salary              | -15000.00                 |
| Electricity               | -3000.00                  |
---------------------------------------------------------

การคืนค่าจากฟังก์ชันด้วยคำสั่ง return

def average(items):
    my_sum = 0.0
    for item in items:
        my_sum = my_sum + item
    return my_sum / len(items)
student_heights = [163.5, 150.0, 167.0, 161.25, 170.0]

avg = average(student_heights)

print(f'The average of student heights is {avg:8.2f}.')
The average of student heights is   162.35.

แบบฝึกหัด 15.2

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

จงนิยามฟังก์ชันที่คำนวณค่าความแปรปรวนของข้อมูล $x_1, x_2, x_3, \ldots, x_N$ ซึ่งการคำนวณจะเป็นไปตามสูตรต่อไปนี้

\[\mathrm{Var}(x_1, x_2, x_3, \ldots, x_N) = \frac{1}{N - 1} \sum_{k = 1}^{N} \left( x_k - \bar{x} \right)^2\]

เมื่อ $\bar{x}$ คือค่าเฉลี่ยของข้อมูล ซึ่งสามารถคำนวณได้จากฟังก์ชัน average ซึ่งได้เตรียมไว้ให้แล้วครับ

**คำเตือน:** เมื่อคำนวณค่าความแปรปรวนได้แล้ว อย่าลืมใช้คำสั่ง return เพื่อคืนผลลัพธ์ออกไปด้วยนะครับ

# ฟังก์ชันสำหรับคำนวณค่าเฉลี่ย
def average(items):
    my_sum = 0.0
    for item in items:
        my_sum = my_sum + item
    return my_sum / len(items)

# ฟังก์ชันสำหรับคำนวณค่าความแปรปรวน
def variance(items):
    return (1/len(items)) * sum([(item - average(items))**2 for item in items])

เมื่อเขียนโปรแกรมเสร็จแล้ว ท่านสามารถทดสอบโปรแกรมได้ตรงนี้ครับ

student_heights = [163.5, 150.0, 167.0, 161.25, 170.0]

avg = average(student_heights)
var = variance(student_heights)

print(f'The average of student heights is {avg:8.2f}.')
print(f'The variance of student heights is {var:8.2f}.')
The average of student heights is   162.35.
The variance of student heights is    47.04.

การหยุดการทำงานของฟังก์ชันด้วยคำสั่ง return

\[\begin{eqnarray} 0! & = & 1 \\ 1! & = & 1 \\ n! & = & 1 \times 2 \times 3 \times \ldots \times n \qquad \textrm{เมื่อ $n > 1$} \end{eqnarray}\]
# คำนวณค่า factorial ของจำนวนเต็ม n
def factorial(n):
    
    # หากค่า n เป็น 0 หรือ 1
    if n in [0, 1]:
        # ให้คืนค่า 1 กลับไป แล้วหยุดการทำงานทันที
        return 1
    
    # วนลูปคูณเลข
    result = 1.0
    for i in range(2, n + 1):
        result = result * i
        
    # คืนผลลัพธ์กลับออกไป
    return result
print(factorial(0))
print(factorial(20))
1
2.43290200817664e+18
def print_student(name, age, height):
    
    # หาก age มีค่า 60 ขึ้นไป
    if age >= 60:
        print('--Senior--')
        print()
        
        # หยุดการทำงานของฟังก์ชัน
        return
    
    print(f'Name   : {name}')
    print(f'Age    : {age:d} y/o')
    print(f'Height : {height:6.2f} cm')
    print()
student_scores = [
    ('John', 13, 165.5), ('Mary', 14, 173.0), ('Cindy', 12, 163.5),
    ('Magdalene', 65, 165.5), ('Eugene', 15, 177.0), ('Rupert', 12, 153.5)
]

for name, age, height in student_scores:
    print_student(name, age, height)
Name   : John
Age    : 13 y/o
Height : 165.50 cm

Name   : Mary
Age    : 14 y/o
Height : 173.00 cm

Name   : Cindy
Age    : 12 y/o
Height : 163.50 cm

--Senior--

Name   : Eugene
Age    : 15 y/o
Height : 177.00 cm

Name   : Rupert
Age    : 12 y/o
Height : 153.50 cm

การทำให้โค้ดอ่านง่ายขึ้น

แบบฝึกหัด 15.4: การตั้งชื่อให้สื่อความหมาย

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

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

# คำนวณค่าเฉลี่ยของข้อมูล
def average(items):
    
    # ผลรวมของข้อมูล
    summation = 0.0
    
    # ไล่ลูปข้อมูลแต่ละตัว
    for num in items:
        summation = summation + num
        
    return summation / len(items)



# คำนวณค่าความแปรปรวนของข้อมูล
def variance(xs):
    
    # ถ้าข้อมูลมีแค่ตัวเดียวหรือไม่มีเลย ให้ตอบ 0
    if len(xs) <= 1:
        return 0.0
    
    # ค่าเฉลี่ยของข้อมูล
    a = average(xs)
    
    # ผลรวมของระยะทาง
    s = 0.0
    
    # ไล่ลูปข้อมูลแต่ละตัว
    for x in xs:
        s = s + (x - a)**2
        
    return s / (len(xs) - 1)

เมื่อท่านได้แก้ไขชื่อต่างๆ เรียบร้อยแล้ว ท่านสามารถทดสอบโปรแกรมของท่านได้ตรงนี้ครับ

student_heights = [163.5, 150.0, 167.0, 161.25, 170.0]

# ค่าเฉลี่ยของความสูง
avg = average(student_heights)

# ค่าความแปรปรวนของความสูง
var = variance(student_heights)

print(f'Student heights = {student_heights}')
print(f'The average is {avg:9.2f}.')
print(f'The variance is {var:8.2f}.')
Student heights = [163.5, 150.0, 167.0, 161.25, 170.0]
The average is    162.35.
The variance is    58.80.

การรีแฟคเตอร์โค้ด

แบบฝึกหัด 15.5: การรีแฟคเตอร์โค้ด

ถ้าเราสังเกตฟังก์ชัน print_students ด้านล่างนี้ จะพบว่าฟังก์ชันนี้มีพารามิเตอร์ 9 ตัว ในแบบฝึกหัดนี้ เราจะมาหัดปรับโค้ดที่มีพารามิเตอร์เยอะๆ ให้อ่านง่ายขึ้นด้วยการรีแฟคเตอร์โค้ดกันครับ

พารามิเตอร์ในโค้ดที่ด้านล่างนี้สามารถแบ่งกลุ่มได้เป็นกี่กลุ่มครับ?

def print_students(name1, name2, name3, age1, age2, age3, height1, height2, height3):
    print(f'Name   : {name1}')
    print(f'Age    : {age1:d} y/o')
    print(f'Height : {height1:6.2f} cm')
    print()
    print(f'Name   : {name2}')
    print(f'Age    : {age2:d} y/o')
    print(f'Height : {height2:6.2f} cm')
    print()
    print(f'Name   : {name3}')
    print(f'Age    : {age3:d} y/o')
    print(f'Height : {height3:6.2f} cm')
    print()

ท่านลองยุบฟังก์ชัน print_students ที่ด้านบนให้กลายเป็นฟังก์ชัน print_student ที่รับข้อมูลของนักเรียนเพียงคนเดียวดูครับ

def print_student(name, age, height):
    print(f'Name   : {name}')
    print(f'Age    : {age:d} y/o')
    print(f'Height : {height:6.2f} cm')
    print()

กิจกรรม 16: การโปรแกรมเชิงวัตถุ

pi = 3.1415926

# คำนวณพื้นที่วงกลม
def area_circle(circle):
    return pi * circle['radius']**2

# คำนวณเส้นรอบวงของวงกลม
def circumference_circle(circle):
    return 2 * pi * circle['radius']

# หาค่าสีของวงกลม
def color_circle(circle):
    return circle['color']

# คำนวณพื้นที่จัตุรัส
def area_square(square):
    return square['side']**2

# คำนวณเส้นรอบวงจัตุรัส
def circumference_square(square):
    return 4 * square['side']

# หาค่าสีของจัตุรัส
def color_square(square):
    return square['color']
circle1 = {'radius': 30.0, 'color': 'red'}
circle2 = {'radius': 15.5, 'color': 'green'}
circle3 = {'radius': 10.0, 'color': 'blue'}

square1 = {'side': 10.0, 'color': 'yellow'}
square2 = {'side': 15.0, 'color': 'orange'}
square3 = {'side': 5.75, 'color': 'pink'}

print(f'Area of circle1 = {area_circle(circle1)}')
print(f'Circumference of circle2 = {circumference_circle(circle2)}')
print(f'Color of circle3 = {color_circle(circle3)}')
print()

print(f'Area of square1 = {area_square(square1)}')
print(f'Circumference of square2 = {circumference_square(square2)}')
print(f'Color of square3 = {color_square(square3)}')
Area of circle1 = 2827.43334
Circumference of circle2 = 97.3893706
Color of circle3 = blue

Area of square1 = 100.0
Circumference of square2 = 60.0
Color of square3 = pink

คลาส (class)

class Circle:
    def __init__(self, color, radius):
        self.color = color
        self.radius = radius
pi = 3.1415926

class Circle:
    
    # คอนสตรักเตอร์
    def __init__(self, color, radius):
        
        # กำหนดสีจากพารามิเตอร์
        self.color = color
        
        # กำหนดรัศมีจากพารามิเตอร์
        self.radius = radius
        
    # คำนวณพื้นที่
    def area(self):
        return pi * self.radius**2
    
    # คำนวณเส้นรอบวง
    def circumference(self):
        return 2 * pi * self.radius
    
    # หาค่าสี
    def get_color(self):
        return self.color
circle1 = Circle('red', 30.0)
circle2 = Circle('green', 15.5)
circle3 = Circle('blue', 10.0)
print('Area of circle1 =', circle1.area())
print('Circumference of circle2 =', circle2.circumference())
print('Color of circle3 =', circle3.get_color())
Area of circle1 = 2827.43334
Circumference of circle2 = 97.3893706
Color of circle3 = blue

แบบฝึกหัด 16.1

คราวนี้เราจะมานิยามคลาส Square กันบ้างนะครับ คลาส Square จะมีคุณสมบัติ 2 ตัว คือ side (ความยาวด้าน) และ color (สี) และมีเมธอด 3 ตัว คือ area (คำนวณพื้นที่), circumference (คำนวณเส้นรอบวง), และ get_color (หาค่าสี) ครับ

class Square:
    
    # คอนสตรักเตอร์
    def __init__(self, color, side):
        
        # กำหนดค่าสี
        self.color = color
        
        # กำหนดความยาวด้าน
        self.side = int(side)
        
    # คำนวณพื้นที่
    def area(self):
        return self.side ** 2
        
    # คำนวณเส้นรอบวง
    def circumference(self):
        return self.side * 4
        
    # หาค่าสี
    def get_color(self):
        return self.color

จากนั้นเราจะมาลองสร้างวัตถุจากคลาส Square กันดูบ้างครับ

square1 = Square('red', 10.0)
square2 = Square('green', 15.0)
square3 = Square('blue', 5.75)

และมาลองเรียกเมธอดของ Square กันดูครับ

print('Area of square1 =', square1.area())
print('Circumference of square2 =', square2.circumference())
print('Color of square3 =', square3.get_color())
Area of square1 = 100
Circumference of square2 = 60
Color of square3 = blue

การถ่ายทอดคุณสมบัติของคลาส (Inheritance)

class Shape:
    
    def __init__(self, color):
        self.color = color
        
    # หาค่าสี
    def get_color(self):
        return self.color
class Circle(Shape):
    
    def __init__(self, color, radius):
        
        # นี่คือวิธีการเรียกคอนสตรักเตอร์ของคลาสแม่ โดยเราจะต้องป้อนพารามิเตอร์ที่จำเป็นสำหรับคลาสแม่เข้าไปด้วย
        super(Circle, self).__init__(color)
        
        # หลังจากนั้นให้เรากำหนดคุณสมบัติของคลาสลูกตามปกติ
        self.radius = radius
        
    # คำนวณพื้นที่
    def area(self):
        return pi * self.radius**2
    
    # คำนวณเส้นรอบวง
    def circumference(self):
        return 2 * pi * self.radius
my_circle = Circle('red', 30.0)
print('Area =', my_circle.area())
print('Circumference =', my_circle.circumference())
print('Color =', my_circle.get_color())
Area = 2827.43334
Circumference = 188.495556
Color = red

แบบฝึกหัด 16.2

จงออกแบบคลาส Square ให้เป็นคลาสลูกของ Shape โดยเลียนแบบจากแบบฝึกหัด 16.1

class Square(Shape):
    
    def __init__(self, color, side):
        
        # เรียกใช้คอนสตรักเตอร์ของคลาสแม่ อย่าลืมป้อนพารามิเตอร์ที่จำเป็นเข้าไปด้วย
        super().__init__(color)
        
        # กำหนดคุณสมบัติของคลาสลูกตามปกติ
        self.side = int(side)
        
    # คำนวณพื้นที่
    def area(self):
        return self.side ** 2
        
    # คำนวณเส้นรอบวง
    def circumference(self):
        return self.side * 4

อย่าลืมทดสอบนะครับว่า โค้ดนี้ใช้งานได้จริงหรือเปล่า

my_square = Square('red', 10.0)
print('Area =', my_square.area())
print('Circumference =', my_square.circumference())
print('Color =', my_square.get_color())
Area = 100
Circumference = 40
Color = red

แบบฝึกหัด 16.3

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

ในโจทย์ข้อนี้ เราจะให้ท่านออกแบบคลาสทั้งหมด 3 คลาสด้วยกัน โดยมีคลาสแม่คือคลาสรีโมทคอนโทรล RemoteControl และมีคลาสลูกอีก 2 คลาสคือ คลาสรีโมทโทรทัศน์ TVRemoteControl และคลาสรีโมทแอร์ ACRemoteControl

16.3.1 คลาสแม่ RemoteControl

คลาสแม่สามารถเปิดและปิดสวิตช์ได้ โดยจะสลับค่าของคุณสมบัติ power

class RemoteControl:
    
    def __init__(self):
        
        # ปิดสวิตช์ของอุปกรณ์ไฟฟ้าไว้ก่อน
        self.power = False
        
    # สลับสวิตช์เปิด/ปิด
    def toggle_power(self):

        print(f'Toggle power: {self.power} -> ', end='')
        
        # สลับค่าของคุณสมบัติ power (คำใบ้: คำสั่ง not)
        self.power = not self.power
        
        print(f'{self.power}')

ทดสอบกันนิดนึงว่า คลาสนี้ใช้ได้หรือเปล่า

rctrl = RemoteControl()

# ค่าสวิตช์ควรจะสลับไปสลับมา
rctrl.toggle_power()
rctrl.toggle_power()
rctrl.toggle_power()
Toggle power: False -> True
Toggle power: True -> False
Toggle power: False -> True

16.3.2 คลาสลูก TVRemoteControl: รีโมทโทรทัศน์

คลาส TVRemoteControl จะเปลี่ยนช่องได้ เพิ่มเสียงได้ และลดเสียงได้ โดย

class TVRemoteControl(RemoteControl):
    
    def __init__(self):
        
        # เรียกคอนสตรักเตอร์ของคลาสแม่
        super().__init__()
        
        # กำหนดคุณสมบัติเฉพาะของคลาสลูก
        self.channel = 0
        self.volume = 5
        
    # เปลี่ยนช่องให้เป็นช่องใหม่ที่กำหนด
    def change_channel(self, new_channel):
        
        print(f'Change channel: {self.channel} -> ', end='')
        
        # เปลี่ยนช่อง โดยช่องจะต้องเป็นจำนวนเต็มอยู่ในช่วง 0 ถึง 9 ไม่เช่นนั้นก็จะไม่เปลี่ยนช่อง
        if 0 <= new_channel <= 9:
            self.channel = new_channel
        
        print(f'{self.channel}')
        
    # เพิ่มเสียง
    def increase_volume(self):
        
        print(f'Increase volume: {self.volume} -> ', end='')
        
        # เพิ่มเสียง โดยค่าระดับเสียงต้องไม่เกิน 10
        if self.volume < 10:
            self.volume += 1
        
        print(f'{self.volume}')
        
    # ลดเสียง
    def decrease_volume(self):
        
        print(f'Decrease volume: {self.volume} -> ', end='')
        
        # ลดเสียง โดยค่าระดับเสียงต้องไม่ต่ำกว่า 0
        if self.volume > 0:
            self.volume -= 1
        
        print(f'{self.volume}')

ทดสอบโค้ดกันหน่อยครับ

tvremote = TVRemoteControl()

# ลองเปลี่ยนช่องตามปกติ
tvremote.change_channel(1)
tvremote.change_channel(2)
print()

# ลองเปลี่ยนช่องที่เป็นไปไม่ได้ ช่องไม่ควรจะเปลี่ยน
tvremote.change_channel(-1)
tvremote.change_channel(11)
print()

# ลองเพิ่มเสียงจนหลุดช่วง ระดับเสียงไม่ควรจะเพิ่มอีก
for i in range(11):
    tvremote.increase_volume()
print()
    
# ลองลดเสียงไปจนหลุดช่วง ระดับเสียงไม่ควรจะลดอีก
for i in range(11):
    tvremote.decrease_volume()
Change channel: 0 -> 1
Change channel: 1 -> 2

Change channel: 2 -> 2
Change channel: 2 -> 2

Increase volume: 5 -> 6
Increase volume: 6 -> 7
Increase volume: 7 -> 8
Increase volume: 8 -> 9
Increase volume: 9 -> 10
Increase volume: 10 -> 10
Increase volume: 10 -> 10
Increase volume: 10 -> 10
Increase volume: 10 -> 10
Increase volume: 10 -> 10
Increase volume: 10 -> 10

Decrease volume: 10 -> 9
Decrease volume: 9 -> 8
Decrease volume: 8 -> 7
Decrease volume: 7 -> 6
Decrease volume: 6 -> 5
Decrease volume: 5 -> 4
Decrease volume: 4 -> 3
Decrease volume: 3 -> 2
Decrease volume: 2 -> 1
Decrease volume: 1 -> 0
Decrease volume: 0 -> 0

16.3.3 คลาสลูก ACRemoteControl: รีโมทแอร์

คลาส ACRemoteControl จะเพิ่มระดับอุณหภูมิได้ ลดระดับอุณหภูมิได้ และเปิด/ปิดการสวิงใบพัดได้ ทั้งนี้อุณหภูมิจะต้องอยู่ในช่วง 15-35 องศาเซลเซียสเท่านั้น

class ACRemoteControl(RemoteControl):
    
    def __init__(self):
        
        # เรียกคอนสตรักเตอร์ของคลาสแม่
        super().__init__()
        
        # กำหนดคุณสมบัติเฉพาะของคลาสลูก
        self.temperature = 25
        self.fan_swing = True
        
    # เพิ่มอุณหภูมิ
    def increase_temperature(self):
        
        print(f'Increase temperature: {self.temperature} -> ', end='')
        
        # เพิ่มอุณหภูมิ โดยค่าระดับอุณหภูมิต้องไม่เกิน 35
        if self.temperature < 35:
            self.temperature += 1
        
        print(f'{self.temperature}')
        
    # ลดอุณหภูมิ
    def decrease_temperature(self):
        
        print(f'Decrease temperature: {self.temperature} -> ', end='')
        
        # ลดอุณหภูมิ โดยค่าระดับเสียงต้องไม่ต่ำกว่า 15
        if self.temperature > 15:
            self.temperature -= 1
        
        print(f'{self.temperature}')
        
    # สลับการเปิด/ปิดสวิงใบพัด
    def toggle_fan_swing(self):
        
        print(f'Toggle fan swing: {self.fan_swing} -> ', end='')
        
        # สลับการเปิด/ปิดสวิงใบพัด (คำใบ้: คำสั่ง not)
        self.fan_swing = not self.fan_swing
        
        print(f'{self.fan_swing}')

ลองทดสอบโค้ดด้านบนดูนะครับ ว่าทำงานได้หรือเปล่า

acremote = ACRemoteControl()

# ลองสลับสวิตช์การสวิงพัดลม
acremote.toggle_fan_swing()
acremote.toggle_fan_swing()
acremote.toggle_fan_swing()
print()

# ลองเพิ่มอุณหภูมิจนหลุดช่วง ระดับอุณหภูมิไม่ควรจะเพิ่มอีก
for i in range(12):
    acremote.increase_temperature()
print()
    
# ลองลดอุณหภูมิไปจนหลุดช่วง ระดับอุณหภูมิไม่ควรจะลดอีก
acremote.temperature = 20
for i in range(12):
    acremote.decrease_temperature()
Toggle fan swing: True -> False
Toggle fan swing: False -> True
Toggle fan swing: True -> False

Increase temperature: 25 -> 26
Increase temperature: 26 -> 27
Increase temperature: 27 -> 28
Increase temperature: 28 -> 29
Increase temperature: 29 -> 30
Increase temperature: 30 -> 31
Increase temperature: 31 -> 32
Increase temperature: 32 -> 33
Increase temperature: 33 -> 34
Increase temperature: 34 -> 35
Increase temperature: 35 -> 35
Increase temperature: 35 -> 35

Decrease temperature: 20 -> 19
Decrease temperature: 19 -> 18
Decrease temperature: 18 -> 17
Decrease temperature: 17 -> 16
Decrease temperature: 16 -> 15
Decrease temperature: 15 -> 15
Decrease temperature: 15 -> 15
Decrease temperature: 15 -> 15
Decrease temperature: 15 -> 15
Decrease temperature: 15 -> 15
Decrease temperature: 15 -> 15
Decrease temperature: 15 -> 15

กิจกรรม 17: การเวียนเกิด (Recursion)

ฟังก์ชันเวียนเกิด (recursive function)

\[\mathrm{factorial}(n) = \begin{cases} n \times \mathrm{factorial}(n - 1) & \textrm{เมื่อ } n > 0 \\ 1 & \textrm{มิฉะนั้น} \end{cases}\] \[\mathrm{factorial}(5) = 5 \times \mathrm{factorial}(4) \\ = 5 \times 4 \times \mathrm{factorial}(3) \\ = 5 \times 4 \times 3 \times \mathrm{factorial}(2) \\ = 5 \times 4 \times 3 \times 2 \times \mathrm{factorial}(1) \\ = 5 \times 4 \times 3 \times 2 \times 1 \times \underline{\mathrm{factorial}(0)} \\ = 5 \times 4 \times 3 \times 2 \times 1 \times 1\]
# ฟังก์ชันแฟคทอเรียล
def factorial(n):
    # เงื่อนไขการหยุดทำงาน
    if n == 0:
        return 1
    # ส่วนเวียนเกิด
    else:
        return n * factorial(n - 1)     # สังเกตว่าจะมีการเรียกฟังก์ชัน factorial ซ้ำที่ตรงนี้
factorial(10)
3628800

การจดจำรายทาง (memoization)

\[\mathrm{fibonacci}(n) = \begin{cases} \mathrm{fibonacci}(n - 1) + \mathrm{fibonacci}(n - 2) & n > 1 \\ 0 & n = 0 \\ 1 & n = 1 \end{cases}\] \[\mathrm{fibonacci}(4) = \mathrm{fibonacci}(3) + \mathrm{fibonacci}(2) \\ = \left[ \mathrm{fibonacci}(2) + \mathrm{fibonacci}(1) \right] + \left[ \mathrm{fibonacci}(0) + \mathrm{fibonacci}(1) \right] \\ = \left[ \left[ \mathrm{fibonacci}(1) + \mathrm{fibonacci}(0) \right] + \mathrm{fibonacci}(1) \right] + \left[ \mathrm{fibonacci}(0) + \mathrm{fibonacci}(1) \right] \\ = ((1 + 0) + 1) + (0 + 1) \\ = 3\]
def fibonacci(n):
    if n == 0 or n == 1:
        return n
    else:
        return fibonacci(n - 1) + fibonacci(n - 2)
fibonacci(4)
3
fibonacci_memory = {}

def fibonacci_memo(n):
    if n not in fibonacci_memory:
        if n == 0 or n == 1:
            fibonacci_memory[n] = n
        else:
            fibonacci_memory[n] = fibonacci_memo(n - 1) + fibonacci_memo(n - 2)
    return fibonacci_memory[n]
fibonacci_memo(4)
3
fibonacci_memo(1000)
43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875
fibonacci_memory.clear()
for i in range(len(fibonacci_memory)):
    print(fibonacci_memory[i])
fibonacci_memo(5)
for i in range(len(fibonacci_memory)):
    print(fibonacci_memory[i])
0
1
1
2
3
5

กิจกรรม 18: โจทย์เสริมทักษะ

  1. จงเขียนโปรแกรมที่คำนวณตัวเลข Fibonacci $ F(n) $ ซึ่งเป็นตามนิยามต่อไปนี้ \(F(n) = \begin{cases} F(n - 1) + F(n - 2) & n \geq 2 \\ n & \\ \end{cases}\) แล้วทดลองพิมพ์ค่าของ $F(5)$ ออกมา

**คำใบ้:** ขอแนะนำให้ใช้การจดจำรายทาง (memoization) เพื่อความรวดเร็วในการคำนวณ

memory_fibonacci = {}    # สำหรับท่านที่เลือกใช้วิธีการจดจำรายทาง (memoization)

def fibonacci(n):
    if n not in memory_fibonacci:
        if n == 0 or n == 1:
            memory_fibonacci[n] = n
        else:
            memory_fibonacci[n] = fibonacci(n - 1) + fibonacci(n - 2)
    # print(memory_fibonacci)
    return memory_fibonacci[n]
fibonacci(9)
34
  1. จงเขียนโปรแกรมเพื่อคำนวณฟังก์ชันของ Ackermann $ A(m, n) $ เมื่อ \(A(m, n) = \begin{cases} n + 1 & \textrm{เมื่อ } m = 0 \\ A(m - 1, 1) & \textrm{เมื่อ } m > 0 \textrm{ และ } n = 0 \\ A(m - 1, A(m, n - 1)) & \textrm{มิฉะนั้น} \end{cases}\) แล้วทดลองพิมพ์ค่าของ $A(1, 2)$ ออกมา

**คำใบ้:** ขอแนะนำให้ใช้การจดจำรายทาง (memoization) เพื่อความรวดเร็วในการคำนวณ

memory_ackermann = {}    # สำหรับท่านที่เลือกใช้วิธีการจดจำรายทาง (memoization)

def ackermann(m, n):
    if (m, n) not in memory_ackermann:
        if m == 0:
            memory_ackermann[(m, n)] = n + 1
        elif m > 0 and n == 0:
            memory_ackermann[(m, n)] = ackermann(m - 1, 1)
        elif m > 0 and n > 0:
            memory_ackermann[(m, n)] = ackermann(m - 1, ackermann(m, n - 1))
    # print(memory_ackermann)
    return memory_ackermann[(m, n)]
ackermann(3, 5)
253
  1. จงเขียนโปรแกรมเพื่อหาความถี่ของตัวอักษรแต่ละตัวที่อยู่ในสตริง gettysburg_address
gettysburg_address = 'Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal. Now we are engaged in a great civil war, testing whether that nation, or any nation so conceived and so dedicated, can long endure. We are met on a great battle-field of that war. We have come to dedicate a portion of that field, as a final resting place for those who here gave their lives that that nation might live. It is altogether fitting and proper that we should do this. But, in a larger sense, we can not dedicate -- we can not consecrate -- we can not hallow -- this ground. The brave men, living and dead, who struggled here, have consecrated it, far above our poor power to add or detract. The world will little note, nor long remember what we say here, but it can never forget what they did here. It is for us the living, rather, to be dedicated here to the unfinished work which they who fought here have thus far so nobly advanced. It is rather for us to be here dedicated to the great task remaining before us -- that from these honored dead we take increased devotion to that cause for which they gave the last full measure of devotion -- that we here highly resolve that these dead shall not have died in vain -- that this nation, under God, shall have a new birth of freedom -- and that government of the people, by the people, for the people, shall not perish from the earth.'
charfreq = {}

# นับความถี่ของแต่ละตัวอักษร เก็บค่าความถี่ลงในดิกชันนารี charfreq
for char in gettysburg_address:
    if char not in charfreq:
        charfreq[char] = 1
    else:
        charfreq[char] += 1

# พิมพ์ดิกชันนารีนั้นออกหน้าจอ
print(charfreq)
{'F': 1, 'o': 93, 'u': 21, 'r': 79, ' ': 277, 's': 44, 'c': 31, 'e': 165, 'a': 102, 'n': 76, 'd': 58, 'v': 24, 'y': 10, 'g': 27, 'f': 26, 't': 124, 'h': 80, 'b': 13, 'i': 65, ',': 22, 'w': 26, 'L': 1, 'p': 15, 'l': 41, 'm': 13, 'q': 1, '.': 10, 'N': 1, 'W': 2, '-': 15, 'I': 3, 'B': 1, 'T': 2, 'k': 3, 'G': 1}
  1. ถ้าวันที่ 23 ธันวาคม 2018 เป็นวันเสาร์ จงเขียนโปรแกรมเพื่อรับวันที่ 2 ชุดในรูปแบบของ ddmmyyyy (เช่น 23122018 สำหรับวันที่ 23 ธันวาคม 2018) แล้วคำนวณว่าในระหว่างนั้นมีวันเสาร์กี่วัน
    • คำใบ้ 1: อย่าลืมคำนึงถึงปีอธิกสุรทิน (leap year) ซึ่งเป็นปีที่มี 366 วันด้วย
    • คำใบ้ 2: ปีอธิกสุรทินคือปี ค.ศ. ที่หาร 4 ลงตัว แต่หาร 100 ไม่ลงตัว
    • คำใบ้ 3: การตรวจสอบว่าหารลงตัวหรือไม่ ให้ใช้ตัวดำเนินการคณิตศาสตร์ % (modulo) เช่น 4 % 3 = 1 ในขณะที่ 4 % 2 = 0 เป็นต้น
import datetime
import math
from_date="20122018"
to_date="23122020"

def calc_number_of_saturdays(fromdate, todate):
  index_date="23122018"
  date0=datetime.datetime.strptime(index_date, "%d%m%Y")
  date1=datetime.datetime.strptime(from_date, "%d%m%Y")
  date2=datetime.datetime.strptime(to_date, "%d%m%Y")
  return (abs((date0-date1).days)+abs((date0-date2).days))//7
print(calc_number_of_saturdays(from_date, to_date))
104
  1. สมมติว่ามีแผนที่เขาวงกตซึ่งเขียนอยู่ในรูปของ list ของ list ซึ่งมีรูปร่างเป็นสี่เหลี่ยมผืนผ้าหรือสี่เหลี่ยมจัตุรัส เช่น
     chaosmap1 = [
         '*******',
         '*     *',
         '* *** *',
         '* *** *',
         '* *** *',
         '*S***G*'
     ]
    

    my answer =

(5, 1) (4, 1) (3, 1) (2, 1) (1, 1) (1, 2) (1, 3) (1, 4) (1, 5) (2, 5) (3, 5) (4, 5) (5, 5)

โดยตัวอักษรแต่ละตัวจะมีความหมายดังนี้

ให้เขียนโปรแกรมเพื่อหา**เส้นทางที่สั้นที่สุด** จากจุดเริ่มต้นไปยังเส้นชัย เช่น เส้นทางสั้นที่สุดของ chaosmap1 จะเป็นดังนี้

    Route:
    (5, 1) <START>
    (4, 1)
    (3, 1)
    (2, 1)
    (1, 1)
    (1, 1)
    (1, 2)
    (1, 3)
    (1, 4)
    (1, 5)
    (2, 5)
    (3, 5)
    (4, 5)
    (5, 5) <GOAL>

**คำใบ้:** ขอแนะนำให้ใช้วิธีการจดจำรายทาง (memoization) เพื่อความรวดเร็วในการคำนวณ

chaosmap1 = [
    '*******',
    '*     *',
    '* *** *',
    '* *** *',
    '* *** *',
    '*S***G*'
]

chaosmap2 = [
    '*************S*******',
    '*             *     *',
    '* * *** ******* * * *',
    '* * *   *       * * *',
    '* * ***** ******* * *',
    '* *     *   *   * * *',
    '******* *** * *** ***',
    '*         * *   *   *',
    '* * ******* * * *** *',
    '* * *       * * *   *',
    '* *** ********* * * *',
    '*   *           * * *',
    '* * *** ********* * *',
    '* *   * *       * * *',
    '*** * * * ***** * ***',
    '*   * * * *   * *   *',
    '* ***** * * *** *** *',
    '*       * * *   *   *',
    '* ******* * * *** * *',
    '*   *     *       * *',
    '*****G***************'
]


memory_traverse = {}   # สำหรับท่านที่เลือกใช้วิธีการจดจำรายทาง (memoization)
memory_path = {}
best_path = []

# A* search algorithm
def traverse(chaosmap, show_result=True):
    memory_path.clear()
    best_path.clear()
    start_pos = None
    goal_pos = None
    for y in range(len(chaosmap)):
        for x in range(len(chaosmap[y])):
            if chaosmap[y][x] == 'S':
                start_pos = (x, y)
                memory_path[(x, y)] = 0
            elif chaosmap[y][x] == 'G':
                goal_pos = (x, y)
    if start_pos is None or goal_pos is None:
        return False
    queues = []
    queues.append(start_pos)
    while len(queues) > 0:
        cur_pos = queues.pop(0)
        if cur_pos == goal_pos:
            break
        for direction in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
            new_pos = (cur_pos[0] + direction[0], cur_pos[1] + direction[1])
            if (new_pos in memory_traverse) and memory_traverse[new_pos] is False:
                continue
            else:
                memory_traverse[new_pos] = False
                if new_pos[0] < 0 or new_pos[0] >= len(chaosmap[0]) or new_pos[1] < 0 or new_pos[1] >= len(chaosmap):
                    continue
                if chaosmap[new_pos[1]][new_pos[0]] == '*':
                    continue
                if new_pos in memory_path:
                    continue
                memory_traverse[new_pos] = True
                memory_path[new_pos] = memory_path[cur_pos] + 1
                queues.append(new_pos)

    # select best path in memory_path
    cur_pos = goal_pos
    while cur_pos != start_pos:
        best_path.append(cur_pos)
        for direction in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
            new_pos = (cur_pos[0] + direction[0], cur_pos[1] + direction[1])
            if new_pos in memory_path and memory_path[new_pos] == memory_path[cur_pos] - 1:
                cur_pos = new_pos
                break
    best_path.append(start_pos)
    best_path.reverse()
    show_result and print('Route:\n'+'\n'.join([f'{str((x, y))}{" <START>" if best_path.index((x,y)) == 0 else ""}{" <GOAL>" if best_path.index((x,y)) == len(best_path) - 1 else ""}' for x, y in best_path]))

class Checker:
    """
    Checking if the path is valid
    """
    def __init__(self, **kwargs) -> None:
        """Initialize the checker"""
        self.name = kwargs.get('name', 'Untitled')
        self.chaosmap = kwargs.get('chaosmap', [])
        self.memory_path = kwargs.get('memory_path', {})
        self.best_path = kwargs.get('best_path', [])
        self.wall = '*'
        self.start = 'S'
        self.goal = 'G'
        self.walkpath = ' '
        self.draw_wall = '████'
        self.draw_walkpath = '    '
        self.draw_walkpath_walked = '░░░░'
        self.draw_start = 'SSSS'
        self.draw_goal = 'GGGG'
        self.width = len(self.draw_wall)
    def __str__(self) -> str:
        """Return visualized chaosmap without any path.

        Returns:
            str: visualized chaosmap without any path.
        """
        return self.__draw()
    def __draw(self, **kwargs) -> str:
        """Return visualized chaosmap with path.
        
        Args:
            **kwargs:
                path (list): Best path list.
                start (tuple): start character marker.
                goal (tuple): goal character marker.
                wall (str): wall character maker.
                walkpath (str): walkpath character marker.
                draw_wall (str): wall character to be show when needed.
                draw_walkpath (str): walkpath character to be show when needed.
                draw_start (str): start character to be show when needed.
                draw_goal (str): goal character to be show when needed.
                blank (str): show instead of unknown charater.
        Returns:
            str: visualized chaosmap with path customized.
        """
        wall = kwargs.get('wall', self.wall)
        draw_wall = kwargs.get('draw_wall', self.draw_wall)
        start = kwargs.get('start', self.start)
        draw_start = kwargs.get('draw_start', self.draw_start)
        goal = kwargs.get('goal', self.goal)
        draw_goal = kwargs.get('draw_goal', self.draw_goal)
        walkpath = kwargs.get('walkpath', self.walkpath)
        draw_walkpath = kwargs.get('draw_walkpath', self.draw_walkpath)
        draw_walkpath_walked = kwargs.get('draw_walkpath_walked', self.draw_walkpath_walked)
        blank = kwargs.get('blank', '    ')
        ret = f'Name: {self.name}\n' + ''.join([f'{str(num):^4}' for num in range(len(self.chaosmap[0]))] + ['\n'])
        for y in range(len(self.chaosmap)):
            for x in range(len(self.chaosmap[y])):
                curr_char = self.chaosmap[y][x]
                coord = {'x': x, 'y': y, 'step': self.memory_path[(x, y)] if (x,y) in self.memory_path else 0}
                if (x, y) in self.best_path and self.chaosmap[y][x] not in [wall, start, goal]:
                    ret += draw_walkpath_walked.format_map(coord)
                elif curr_char == wall:
                    ret += draw_wall.format_map(coord)
                elif curr_char == start:
                    ret += draw_start.format_map(coord)
                elif curr_char == goal:
                    ret += draw_goal.format_map(coord)
                elif curr_char == walkpath:
                    ret += draw_walkpath.format_map(coord)
                else:
                    ret += blank.format_map(coord)
            ret += f'{y:^4}' + '\n'
        return ret
    def draw_trace(self,**kwargs) -> None:
        print(self.__draw(draw_walkpath_walked='{step:>4}', draw_walkpath='    '))
    def draw(self):
        print(self.__draw())
_maze = chaosmap2
checking = {
    'name': 'Random Maze',
    'chaosmap': _maze,
    'memory_path': memory_path,
    'best_path': best_path,
}

traverse(_maze, show_result=False)
map = Checker(**checking)
map.draw_walkpath_walked = '░░░░'
map.draw_walkpath = '    '
map.draw_goal = ' GG '
map.draw_start = ' SS '
map.draw_wall = '////'
map.draw()
Name: Random Maze
 0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16  17  18  19  20 
//////////////////////////////////////////////////// SS //////////////////////////// 0  
////        ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░////░░░░░░░░░░░░        //// 1  
////    ////░░░░////////////    ////////////////////////////░░░░////░░░░////    //// 2  
////    ////░░░░////            ////░░░░░░░░░░░░░░░░░░░░░░░░░░░░////░░░░////    //// 3  
////    ////░░░░////////////////////░░░░////////////////////////////░░░░////    //// 4  
////    ////░░░░░░░░░░░░░░░░░░░░////░░░░░░░░░░░░////            ////░░░░////    //// 5  
////////////////////////////░░░░////////////░░░░////    ////////////░░░░//////////// 6  
////░░░░░░░░░░░░░░░░░░░░░░░░░░░░        ////░░░░////            ////░░░░░░░░░░░░//// 7  
////░░░░////    ////////////////////////////░░░░////    ////    ////////////░░░░//// 8  
////░░░░////    ////░░░░░░░░░░░░░░░░░░░░░░░░░░░░////    ////    ////░░░░░░░░░░░░//// 9  
////░░░░////////////░░░░////////////////////////////////////    ////░░░░////    //// 10 
////░░░░░░░░░░░░////░░░░░░░░░░░░                                ////░░░░////    //// 11 
////    ////░░░░////////////░░░░////////////////////////////////////░░░░////    //// 12 
////    ////░░░░        ////░░░░////░░░░░░░░░░░░░░░░░░░░░░░░░░░░////░░░░////    //// 13 
////////////░░░░////    ////░░░░////░░░░////////////////////░░░░////░░░░//////////// 14 
////░░░░░░░░░░░░////    ////░░░░////░░░░////            ////░░░░////░░░░░░░░░░░░//// 15 
////░░░░////////////////////░░░░////░░░░////    ////////////░░░░////////////░░░░//// 16 
////░░░░░░░░░░░░░░░░░░░░░░░░░░░░////░░░░////    ////░░░░░░░░░░░░////░░░░░░░░░░░░//// 17 
////    ////////////////////////////░░░░////    ////░░░░////////////░░░░////    //// 18 
////            ////░░░░░░░░░░░░░░░░░░░░////        ░░░░░░░░░░░░░░░░░░░░////    //// 19 
//////////////////// GG //////////////////////////////////////////////////////////// 20 

แหล่งที่มา
  • https://tinyurl.com/ybn763lq