บทที่ 3
โครงสร้างข้อมูล (Data Structure) I#

โครงสร้างข้อมูล (data structure) เป็นกลไกที่ใช้สำหรับการจัดเก็บข้อมูลในรูปแบบที่มีโครงสร้าง เพื่อให้สามารถจัดเก็บ ค้นหา และจัดการข้อมูลได้อย่างมีประสิทธิภาพ การเข้าใจโครงสร้างข้อมูลเป็นสิ่งสำคัญอย่างยิ่งสำหรับโปรแกรมเมอร์ โดยเฉพาะอย่างยิ่งผู้ที่สนใจทางด้านวิทยาการข้อมูล (data science) เนื่องจากต้องมีการจัดเก็บ เคลื่อนย้าย และประมวลผลข้อมูลอยู่เป็นประจำ

จุดมุ่งหมายของบทนี้

  • เลือกใช้โครงสร้างข้อมูลประเภทลิสต์ (list) ดิกชันนารี (dictionary) ทูเปิล (tuple) เคาน์เตอร์ (counter) และเซต (set) ให้เข้ากับโจทย์ได้อย่างถูกต้อง

  • การใช้ for และ while ร่วมกับโครงสร้างข้อมูลที่ซับซ้อนขึ้น

  • ใช้เมท็อดของโครงสร้างข้อมูลเพื่อแก้โจทย์ได้อย่างมีประสิทธิภาพ

โครงสร้างข้อมูลมีอยู่หลายประเภท ซึ่งในบทนี้เราจะศึกษาและฝึกปฏิบัติการใช้โครงสร้างข้อมูลประเภทลิสต์ (list) ดิกชันนารี (dictionary) ทูเปิล (tuple) เคาน์เตอร์ (counter) และเซต (set) ในการเก็บข้อมูล การเลือกใช้โครงสร้างข้อมูลต้องพิจารณาถึง 3 ปัจจัยหลัก

  1. ความถูกต้องของข้อมูล: ข้อมูลจะต้องไม่สูญหายหรือบิดเบือนไปจากเจตนาของผู้ใช้งาน เช่น โครงสร้างข้อมูลบางประเภทไม่เก็บข้อมูลที่ซ้ำ หรือไม่เรียงลำดับข้อมูลที่เก็บไว้

  2. ความรวดเร็วในการประมวลผล: โครงสร้างข้อมูลแต่ละประเภทมีวิธีการจัดเก็บข้อมูลที่แตกต่างกัน ทำให้เวลาที่ใช้ในการค้นหาและดึงข้อมูลมาใช้งานจึงต่างกันออกไป

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

ในบทนี้และบทถัดไป เราจะศึกษาวิธีการใช้งานโครงสร้างข้อมูลในแง่มุมต่าง ๆ ดังนี้

  1. วิธีการสร้างโครงสร้างข้อมูล

  2. วิธีการเพิ่มข้อมูลเข้าไปในโครงสร้าง

  3. วิธีการแก้ไขข้อมูลที่อยู่ในโครงสร้าง

  4. วิธีการดึงข้อมูลออกมาโดยการใช้วงวน

  5. วิธีการใช้เมท็อดต่าง ๆ ของโครงสร้างข้อมูล

  6. การพิจารณาข้อดีข้อเสียของโครงสร้างข้อมูลแต่ละประเภท

ลิสต์#

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

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

การสร้างลิสต์ด้วย []#

ลิสต์สามารถสร้างได้โดยใช้เครื่องหมาย [] ในการคร่อมข้อมูล โดยไม่สามารถใช้ () หรือ {} แทนได้

my_first_list = []

ในตัวอย่างนี้ตัวแปร my_first_list เป็นลิสต์เปล่าที่ไม่มีข้อมูลหรือสมาชิกใด ๆ

หากต้องการสร้างลิสต์ที่เป็นค่าคงที่ (literal) กล่าวคือสมาชิกของลิสต์ถูกระบุเอาไว้โดยตรงในโค้ด เราสามารถใช้ [] คร่อมข้อมูลที่ต้องการเก็บ (เรียกว่า สมาชิกของลิสต์) โดยที่ข้อมูลแต่ละตัวจะเป็น int float หรือสตริงก็ได้คั่นด้วยเครื่องหมาย ,

สมมติว่าเราต้องการสร้างลิสต์ที่เก็บยอดขายหกเดือนแรก และสร้างอีกลิสต์เพื่อเก็บชื่อเดือน

sales_list = [12, 10, 5, 2, 5 10]
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June']

ตัวแปร sales_list และ months เป็นลิสต์เก็บ int และสตริงตามลำดับ ซึ่งทั้งสองลิสต์มีสมาชิกทั้งหมด 6 ตัวเท่ากัน

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

sales_list = ['Jan', 12, 'Feb', 10]

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

ชื่อตัวแปรที่เก็บลิสต์#

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

  • ถ้าลิสต์เก็บนามสกุลก็ควรจะตั้งชื่อตัวแปรว่า last_name_list หรือ last_names

  • ถ้าลิสต์เก็บจำนวนคำที่อยู่ในแต่ละประโยค ควรจะตั้งชื่อตัวแปรว่า num_words_list หรือ num_words แต่ว่าจากชื่อ num_words อาจจะกำกวมว่าเก็บไว้เป็น int ตัวเดียวเดี่ยว ๆ หรือไม่

การสร้างลิสต์จากสตริง#

sentence = "Let's go crazy crazy crazy 'til we see the sun"
word_list = sentence.split(' ')

สตริงเมท็อด.split ใช้เวลาเราต้องการเปลี่ยนสตริงให้เป็นลิสต์ โดยที่ต้องกำหนดว่าจะใช้สตริงอะไรแบ่งสมาชิกแต่ละตัว ในตัวอย่างนี้เราใช้ ' ' ช่องว่างในการแบ่ง ในการประมวลผลภาษาธรรมชาติเราใช้คำสั่งนี้บ่อยมากในการตัดคำ ซึ่งก็คือการเปลี่ยนสตริงให้เป็นลิสต์ของคำ

การหาความยาวของลิสต์ (จำนวนสมาชิก)#

ไพทอนมีฟังก์ชันบิวท์อิน len ที่ใช้หาจำนวนสมาชิกของลิสต์ ซึ่งสามารถใช้ได้กับโครงสร้างข้อมูลอื่น ๆ ของไพทอนทั้งหมดอีกด้วย เช่น

sentence = "Let's go crazy crazy crazy 'til we see the sun"
word_list = sentence.split(' ')
len(word_list) #---> 10

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

เข้าถึงสมาชิกในลิสต์โดยใช้ดัชนี#

การเข้าถึงสมาชิกแต่ละตัว เราจะใช้ตัวดำเนินการ [] คร่อมดัชนี และกฎการใช้ดัชนีเหมือนกับสตริงทุกประการ สมาชิกตัวแรกอยู่ที่ดัชนี 0 และสามารถใช้ดัชนีติดลบ ในการนับดัชนีถอยหลังจากท้ายลิสต์ได้ โดยดัชนี -1 หมายถึงสมาชิกตัวสุดท้าย, ดัชนี -2 หมายถึงสมาชิกถัดจากตัวสุดท้ายเป็นต้น

ตัวอย่างเช่น หากเรามีลิสต์ดังนี้

words = ["Let's", 'go', 'crazy', 'crazy', 'crazy', "'til", 'we', 'see', 'the', 'sun']

เราสามารถเข้าถึงสมาชิกในลิสต์ได้โดยใช้ดัชนี เช่น

คำสั่ง

ผลลัพธ์

words[0]

"Let's"

words[1]

'go'

words[-3]

'see'

words[5]

"'til"

ในกรณีที่พยายามเข้าถึงดัชนีที่ไม่อยู่ในช่วงของลิสต์ เช่น

words[20]

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

การหั่นลิสต์#

การหั่นลิสต์ (list slicing) เป็นการดึงข้อมูลบางส่วนจากลิสต์ โดยใช้เครื่องหมาย [] พร้อมระบุช่วงของดัชนีที่ต้องการ การหั่นลิสต์สามารถทำได้ในลักษณะเดียวกับการหั่นสตริง โดยใช้รูปแบบ list[start:end] ซึ่ง start คือดัชนีเริ่มต้น และ end คือดัชนีสุดท้าย (ไม่รวมค่าที่ดัชนี end)

ตัวอย่างการใช้งานการหั่นลิสต์

numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

คำสั่ง

ผลลัพธ์

คำอธิบาย

numbers[2:5]

[2, 3, 4]

ดัชนี 2 ถึง 4 ไม่รวม 5

numbers[:4]

[0, 1, 2, 3]

ดัชนีเริ่มต้นถึง 3 ไม่รวม 4

numbers[5:]

[5, 6, 7, 8, 9]

ดัชนี 5 ถึงสุดท้าย

การหั่นลิสต์เป็นฟังก์ชันที่มีประโยชน์อย่างมากในการจัดการข้อมูล เราสามารถใช้มันในการดึงข้อมูลย่อย ๆ ที่ต้องการได้โดยง่าย

การวนซ้ำบนลิสต์ด้วยคำสั่ง for#

เราสามารถวนซ้ำ (iterate) บนลิสต์ได้ในลักษณะเดียวกับการวนซ้ำบนสตริง โดยสามารถวนซ้ำผ่านสมาชิกของลิสต์โดยตรง หรือวนซ้ำผ่านดัชนีของลิสต์ได้

ตัวอย่างเช่น หากเราต้องการนับจำนวนคำที่สะกดด้วยตัวพิมพ์เล็กทั้งหมดในลิสต์

word_list = ['Rick', 'and', 'Michonne', 'protect', 'Carl']
count = 0
for i in range(len(word_list)):
    word = word_list[i]  # ดึงค่าคำจากดัชนี i
    if word.islower():   # ตรวจสอบว่าคำเป็นตัวพิมพ์เล็กทั้งหมดหรือไม่
        count += 1

เราจะใช้ i เป็นตัวดัชนีในการเข้าถึงสมาชิกแต่ละตัวในลิสต์ โดยดัชนีจะเริ่มต้นที่ 0 และเพิ่มขึ้นเรื่อย ๆ จนถึง len(word_list) - 1 อย่างไรก็ตาม การวนซ้ำแบบนี้ต้องมีการเรียกฟังก์ชัน len(word_list) และต้องเข้าถึงสมาชิกของลิสต์ดัชนีแยกออกมาอีกคำสั่ง word_list[i] ซึ่งอาจทำให้โค้ดยาวและซับซ้อนเกินไป

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

word_list = ['Rick', 'and', 'Michonne', 'protect', 'Carl']
count = 0
for word in word_list:
	if word.is_lower(): # ตรวจสอบว่าสะกดด้วยตัวเล็กหรือไม่ 
		count += 1

โค้ดข้างบนนี้ได้ผลออกมาเหมือนกันทุกประการ เราวนซ้ำโดยตรงบนสมาชิกแต่ละตัวในลิสต์ ซึ่งทำให้โค้ดอ่านง่ายขึ้นและลดความซับซ้อนของการเข้าถึงสมาชิกแต่ละตัว ในการวนรอบแรก word จะมีค่าเป็น word_list[0] ในการวนรอบที่สอง word จะมีค่าเป็น word_list[1] และเลื่อนไปเรื่อย ๆ จนวนผ่านสมาชิกครบทุกตัวในลิสต์ ตัวอย่างนี้ทำให้เห็นได้ว่าเราประหยัดโค้ดได้ค่อนข้างมาก โค้ดดูสะอาดตา และสะดวกสบายในการเขียนมากกว่าการวนซ้ำบนดัชนี

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

word_list = ['Rick', 'and', 'Michonne', 'protect', 'Carl']
count = 0
for word_index, word in enumerate(word_list):
	if word.is_lower(): # ตรวจสอบว่าสะกดด้วยตัวเล็กหรือไม่ 
		count += 1

ฟังก์ชัน enumerate() จะคืนค่าทั้งดัชนีและสมาชิกในลิสต์ โดยในการวนรอบแรก word_index จะมีค่าเป็น 0 และ word จะมีค่าเป็น 'Rick' ในการวนรอบถัดไป word_index จะมีค่าเป็น 1 และ word จะมีค่าเป็น 'and' และต่อไปเรื่อยๆ จนครบทุกสมาชิกในลิสต์

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

ตัวดำเนินการของลิสต์ และฟังก์ชันแบบบิวท์อิน#

ลิสต์ใช้ตัวดำเนินการ และฟังก์ชันแบบบิวท์อิน หลายรูปแบบ ซึ่งคล้ายคลึงกับการทำงานของสตริง

Operator

ความหมาย

ตัวอย่าง

+

ต่อลิสต์ (concatenate)

['a','a'] + ['b','b'] –> ['a','a','b','b']

*

ซ้ำลิสต์

['a','b'] * 3 –> ['a','b','a','b','a','b']

in

ตรวจสอบว่าข้อมูลชิ้นนี้อยู่ในลิสต์หรือไม่

'b' in ['a','b'] –> True

'c' in ['a','b'] –> False

not in

นิเสธของ in

'b' not in ['a','b'] –> False

==

เท่ากันพอดีหรือไม่

['a','b'] == ['a','b'] –> True

['a','b'] == ['a','b','c'] –> False

len(s)

คืนค่าความยาวลิสต์

len(['a','b','c']) –> 3

การเขียนทับสมาชิกในลิสต์ด้วย []#

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

accountant_list = ['เจ', 'เมย์', 'จอย', 'เมย์', 'เมย์', 'มิว']
accountant_list[0] = 'โจ้'
print(accountant_list)
# เอาท์พุต: ['โจ้', 'เมย์', 'จอย', 'เมย์', 'เมย์', 'มิว']

ในตัวอย่างนี้ เราได้เขียนทับสมาชิกตัวแรกของลิสต์ (ดัชนี 0) จาก 'เจ' เป็น 'โจ้'

เรายังสามารถเขียนทับสมาชิกหลายตัวพร้อมกันได้โดยใช้ช่วงของดัชนี เช่น

accountant_list[1:3] = ['แนน', 'ไอซ์']
print(accountant_list)
# เอาท์พุต: ['โจ้', 'แนน', 'ไอซ์', 'เมย์', 'เมย์', 'มิว']

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

เมท็อดของลิสต์#

ลิสต์ของเมท็อดของไพทอน เวอร์ชัน 3.12 มีอยู่ 11 เมท็อด แต่ว่าเมท็อดที่ใช้บ่อย ๆ ในการประมวลผลข้อมูลที่เกี่ยวข้องกับวิทยาการข้อมูล และการประมวลผลภาษาธรรมชาติ มีอยู่ 8 เมท็อด ได้แก่ .append() .extend() .sort() .index() .count() .insert() .pop() .remove()

นอกจากนั้นยังมีฟังก์ชันบิวอินที่ใช้กับลิสต์ได้ 4 ฟังก์ชัน ได้แก่ sort() sum() max() min() ซึ่งเราจะเรียนรู้วิธีการใช้เมท็อดและฟังก์ชันเหล่านี้ในบทนี้

เพิ่มข้อมูลเข้าท้ายลิสต์: .append()#

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

ตัวอย่างการใช้งาน .append()

sales_list = [23, 12, 96] # สร้างลิสต์
sales_list.append(100) # เติมข้อมูลเข้าไป
print(sales_list) 
#เอาท์พุต คือ [23, 12, 96, 100]

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

คุณสมบัติของเมท็อด .append()

  • อินพุต: สมาชิกใหม่ที่ต้องการเพิ่มเข้าไปในลิสต์

  • เอาท์พุต: None (เนื่องจากเมท็อดนี้แก้ไขลิสต์โดยตรง)

เมท็อด .append() เป็นหนึ่งในเมท็อดที่ได้ใช้งานบ่อยที่สุดในลิสต์ โดยเฉพาะในกรณีที่เราต้องการเก็บข้อมูลที่เปลี่ยนแปลงได้ เช่น การเพิ่มข้อมูลเข้าไปในลิสต์ทีละตัวในระหว่างการประมวลผลข้อมูล ตัวอย่างเช่น

numbers = []  # สร้างลิสต์เปล่า
for i in range(5):
    numbers.append(i * 2)  # เพิ่มค่าคูณสองของ i ลงในลิสต์
print(numbers)
# ผลลัพธ์: [0, 2, 4, 6, 8]

ในตัวอย่างนี้ เราใช้ .append() เพื่อเพิ่มข้อมูลเข้าไปในลิสต์ numbers ทีละตัว โดยค่าที่เพิ่มเข้าไปคือผลคูณของ i และ 2 ลิสต์สุดท้ายจะประกอบด้วยค่าคูณสองของตัวเลขตั้งแต่ 0 ถึง 4

นำลิสต์หลาย ๆ ลิสต์มาต่อกัน: .extend()#

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

ตัวอย่างการใช้งาน

sales_first_six_months = [23, 12, 96, 100, 40, 10]
sales_last_six_months = [33, 44, 55, 66, 77, 88]
sales_first_six_months.extend(sales_last_six_months)
print(sales_first_six_months)
# เอาท์พุต: [23, 12, 96, 100, 40, 10, 33, 44, 55, 66, 77, 88]

ในตัวอย่างนี้ สมาชิกจากลิสต์ sales_last_six_months ถูกเพิ่มต่อท้ายลิสต์ sales_first_six_months โดยตรง

คุณสมบัติของเมท็อด .extend() อินพุต: ลิสต์ที่ต้องการคัดลอกข้อมูลมาใส่ เอาท์พุต: None (เนื่องจากเมท็อดนี้แก้ไขลิสต์โดยตรง)

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

เรียงลำดับสมาชิกในลิสต์: sorted(list) และ .sort()#

การเรียงลำดับลิสต์สามารถทำได้สองวิธี คือการใช้ฟังก์ชัน sorted() และการใช้เมท็อด .sort() ทั้งสองวิธีนี้ทำงานคล้ายกัน แต่มีความแตกต่างสำคัญที่ควรทราบ

ฟังก์ชัน sorted()#

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

sales_list = [23, 12, 96, 100]
sorted_sales_list = sorted(sales_list)
print(sorted_sales_list)
# ผลลัพธ์: [12, 23, 96, 100]
print(sales_list)
# ผลลัพธ์: [23, 12, 96, 100]  (ลิสต์ต้นฉบับไม่ถูกเปลี่ยนแปลง)

เมท็อด .sort()#

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

accountant_list = ['เจ', 'เมย์', 'จอย', 'มิว']
accountant_list.sort()
print(accountant_list)
# ผลลัพธ์: ['จอย', 'มิว', 'เจ', 'เมย์']

ในตัวอย่างนี้ ลิสต์ accountant_list ถูกเรียงลำดับใหม่ตามลำดับตัวอักษรโดยตรง

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

หาผลรวม ค่าสูงสุด ต่ำสุด ในลิสต์: sum(list), max(list) และ min(list)#

ฟังก์ชันแบบบิวท์อิน sum(), max() และ min() เป็นฟังก์ชันที่ใช้ในการหาผลรวม ค่าสูงสุด และค่าต่ำสุดของสมาชิกในลิสต์ที่ประกอบด้วยข้อมูลประเภทตัวเลข

ตัวอย่างการใช้งาน

class_size_list = [40, 50, 60, 70, 80]
print(sum(class_size_list))
# เอาท์พุต: 300
print(max(class_size_list))
# เอาท์พุต: 80
print(min(class_size_list))
# เอาท์พุต: 40

ในตัวอย่างนี้ ฟังก์ชัน sum() จะคืนค่าผลรวมของสมาชิกในลิสต์ ส่วนฟังก์ชัน max() จะคืนค่าสมาชิกที่มีค่ามากที่สุด และฟังก์ชัน min() จะคืนค่าสมาชิกที่มีค่าน้อยที่สุด

คุณสมบัติของฟังก์ชันเหล่านี้

  • อินพุต: ลิสต์ที่ประกอบด้วยตัวเลข

  • เอาท์พุต: ผลรวม หรือค่าสูงสุด หรือค่าต่ำสุด

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

string_list = ['apple', 'banana', 'cherry']
print(max(string_list))  # ค่าสูงสุดตามลำดับอักษร
# เอาท์พุต: 'cherry'

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

mixed_list = [10, 'apple', 20]
print(max(mixed_list))  

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

ข้อควรระวังอีกประการ คือฟังก์ชันเหล่านี้ไม่ใช่เมท็อดแต่ว่าเป็นฟังก์ชันแบบบิวท์อิน เพราะฉะนั้นลิสต์ไม่สามารถเรียกใช้ในรูปแบบ ลิสต์.sum() ได้ หากมีการเรียกใช้งานในลักษณะนี้ จะเกิดข้อผิดพลาด AttributeError ซึ่งเป็นข้อผิดพลาดที่เกิดขึ้นเมื่อพยายามเรียกเมท็อดที่ไม่มีอยู่ในข้อมูลประเภทนั้น ๆ

>>> class_size_list.sum()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'sum'

ข้อผิดพลาดนี้เกิดขึ้นเนื่องจากลิสต์ไม่มีเมท็อด sum() ผู้ใช้ควรใช้ฟังก์ชันในตัว sum(class_size_list) แทน

หาดัชนีของสมาชิก: .index()#

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

ตัวอย่างการใช้งาน:

accountant_list = ['เจ', 'เมย์', 'จอย', 'มิว', 'จอย']
print(accountant_list.index('จอย'))
# เอาท์พุต: 2

ในตัวอย่างนี้ ค่าที่เมท็อด .index() คืนกลับมาคือ 2 ซึ่งเป็นดัชนีของสมาชิก 'จอย' ที่ปรากฏตัวแรกในลิสต์

คุณสมบัติของเมท็อด .index()

  • อินพุต: สมาชิกที่ต้องการทราบดัชนี(ตำแหน่งบนลิสต์)

  • เอาท์พุต: ดัชนี

ข้อควรระวัง

  • ก่อนใช้เมท็อดนี้ต้องเตือนความจำตัวเองก่อนว่าดัชนีของตำแหน่งแรกในลิสต์คือ 0 เช่น 'เจ' ในตัวอย่างข้างต้นมีดัชนีเป็น 0 และ 'เมย์' มีดัชนีเป็น 1

  • หากสมาชิกที่ค้นหาปรากฏมากกว่าหนึ่งครั้ง เมท็อด .index() จะคืนค่าดัชนีของสมาชิกตัวแรกที่พบ ซึ่งค้นหาจากซ้ายไปขวาในลิสต์

  • หากสมาชิกที่ค้นหาไม่มีอยู่ในลิสต์ เมท็อดจะทำให้เกิดข้อผิดพลาด ValueError เช่น

>>> accountant_list.index('จิว')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: 'จิว' is not in list

นับจำนวนของสมาชิกที่ระบุมา: .count()#

เมท็อด .count() ใช้สำหรับนับจำนวนครั้งที่สมาชิกที่กำหนดปรากฏอยู่ในลิสต์ เมท็อดนี้จะคืนค่าจำนวนครั้งที่พบ

ตัวอย่างการใช้งาน

accountant_list = ['เจ', 'เมย์', 'จอย', 'เมย์', 'เมย์', 'มิว']
print(accountant_list.count('เมย์'))
# เอาท์พุต: 3

ในตัวอย่างนี้ ค่าที่เมท็อด .count() คืนกลับมาคือ 3 ซึ่งหมายความว่าสมาชิก 'เมย์' ปรากฏอยู่ในลิสต์ทั้งหมด 3 ครั้ง

คุณสมบัติของเมท็อด .count()

  • อินพุต: สมาชิกที่ต้องการนับจำนวนครั้งที่ปรากฏในลิสต์

  • เอาท์พุต: จำนวนครั้งที่สมาชิกนั้นปรากฏในลิสต์

หากสมาชิกที่กำหนดไม่ปรากฏอยู่ในลิสต์ เมท็อด .count() จะคืนค่าเป็น 0 โดยไม่ทำให้เกิดข้อผิดพลาด เช่น

print(accountant_list.count('จิว'))
# ผลลัพธ์: 0

ในกรณีนี้ ค่าที่ได้คือ 0 เนื่องจาก 'จิว' ไม่ปรากฏอยู่ในลิสต์

แก้ไขลิสต์: .insert() .pop() .remove()#

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

แทรกสมาชิกเข้าลิสต์: .insert()#

เมท็อด .insert() ใช้สำหรับแทรกสมาชิกใหม่เข้าไปในตำแหน่งที่กำหนดภายในลิสต์ สมาชิกที่ถูกแทรกจะเลื่อนตำแหน่งสมาชิกที่มีอยู่เดิมต่อไปทางขวา

ตัวอย่างการใช้งาน

accountant_list = ['เจ', 'เมย์', 'จอย', 'มิว']
accountant_list.insert(1, 'เจน')
print(accountant_list)
# ['เจ', 'เจน', 'เมย์', 'จอย', 'มิว']

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

คุณสมบัติของเมท็อด .insert()

  • อินพุต:

    1. ดัชนี (สามารถใช้ negative ดัชนีได้)

    2. สมาชิกใหม่

  • เอาท์พุต: None (เนื่องจากลิสต์ถูกแก้ไขโดยตรง)

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

นำสมาชิกออกจากลิสต์: .remove()#

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

ตัวอย่างการใช้งาน

accountant_list = ['เจ', 'เมย์', 'จอย', 'มิว']
accountant_list.remove('จอย')
print(accountant_list)
# ['เจ', 'เมย์', 'มิว']

ในตัวอย่างนี้ สมาชิก 'จอย' ตัวแรกที่พบในลิสต์จะถูกลบออก

คุณสมบัติของเมท็อด .remove()

  • อินพุต: ค่าของสมาชิกที่ต้องการลบออกจากลิสต์

  • ผลลัพธ์: None

หากไม่มีสมาชิกที่ตรงกับค่าที่ระบุ เมท็อด.remove() จะทำให้เกิดข้อผิดพลาด ValueError เช่น

>>> accountant_list.remove('จิว')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: list.remove(x): x not in list

นำสมาชิกออกจากลิสต์: .pop()#

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

ตัวอย่างการใช้งาน

accountant_list = ['เจ', 'เมย์', 'จอย', 'มิว']
removed_member = accountant_list.pop(2)
print(accountant_list)
# ['เจ', 'เมย์', 'มิว']
print(removed_member)
# จอย

ในตัวอย่างนี้ สมาชิกที่ดัชนี 2 ('จอย') ถูกนำออกจากลิสต์และคืนค่าให้ตัวแปร removed_member

คุณสมบัติของเมท็อด .pop()

  • อินพุต: ดัชนีที่ต้องการนำสมาชิกออก (สามารถใช้ดัชนีติดลบได้)

  • เอาท์พุต: สมาชิกที่ถูกลบออก

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

>>> accountant_list.pop(10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: pop index out of range

ในกรณีที่ไม่มีการระบุดัชนี เมท็อด .pop() จะนำสมาชิกตัวสุดท้ายออกจากลิสต์

ดิกชันนารี (Dictionary)#

ดิกชันนารี หรือที่โปรแกรมเมอร์มักเรียกย่อว่า “ดิกต์ (dict)” เป็นโครงสร้างข้อมูลที่ใช้เก็บคู่ของคีย์ (key) และแวลู (value) โดยคีย์และแวลูสามารถเป็นข้อมูลประเภทใดก็ได้ ดิกชันนารีมีการทำงานคล้ายคลึงกับพจนานุกรมที่เราคุ้นเคย ซึ่งคีย์เปรียบเสมือนคำที่เราต้องการค้นหา และแวลูเปรียบเสมือนความหมายของคำนั้น ๆ อย่างไรก็ตาม ดิกชันนารีในภาษาไพทอนสามารถมีคีย์และแวลูเป็นข้อมูลประเภทใดก็ได้ ไม่จำกัดแค่คำศัพท์และความหมายเหมือนพจนานุกรม

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

ตัวอย่างการใช้ดิกชันนารีในการเก็บข้อมูล ได้แก่

การใช้งาน

คีย์

แวลู

สมุดโทรศัพท์

ชื่อ

เบอร์โทรศัพท์

พจนานุกรมไทย-อังกฤษ

คำศัพท์ภาษาไทย

ความหมายเป็นภาษาอังกฤษ

ทะเบียนนิสิต

รหัสประจำตัวนิสิต

รายวิชาที่ลงทะเบียน

ดัชนีท้ายเล่มหนังสือ

คำสำคัญ

เลขหน้าที่มีคำนั้นปรากฏอยู่

ระบบค้นหา (search engine)

คำ

หมายเลขของเอกสารที่มีคำนั้นอยู่

สถิติของคำในคลังข้อมูล

คำ

จำนวนครั้งที่พบเจอคำนั้นในคลังข้อมูล

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

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

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

การสร้างดิกชันนารีด้วย {}#

ดิกชันนารีถูกสร้างได้ด้วยการใช้ {}

my_first_dict = {}

ตัวแปร my_first_dict เป็นดิกชันนารีเปล่าที่ไม่มีข้อมูลเก็บอยู่เลย

เราสามารถใช้ {} คร่อมข้อมูลคู่คีย์แวลูที่ต้องการเก็บได้โดยตรง โดยใช้รูปแบบดังนี้

ชื่อตัวแปร = {key1: value1, key2: value2, key3: value3, ..., keyn: valuen}

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

name_to_phone_number = {'ตังเม': '088-888-8888', 'เป่าหลง' : '099-999-9999', 'เจนนี่': '011-111-1111'}

ดิกชันนารีนี้เก็บข้อมูลเกี่ยวกับชื่อและหมายเลขโทรศัพท์ในลักษณะของคู่คีย์และแวลู ดังนี้

คีย์

แวลู

‘ตังเม’

‘088-888-8888’

‘เป่าหลง’

‘099-999-9999’

‘เจนนี่’

‘011-111-1111’

ชื่อตัวแปรที่เก็บดิกชันนารี#

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

  • name_to_phone_number เก็บชื่อเป็นคีย์ และเบอร์โทรศัพท์เป็นแวลู

  • word_to_frequency เก็บคำเป็นคีย์ และจำนวนครั้งที่พบเป็นแวลู

  • student_id_to_name เก็บรหัสนักศึกษาเป็นคีย์ และชื่อเป็นแวลู

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

หาจำนวนคู่คีย์แวลูด้วยคำสั่ง len#

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

name_to_phone_number = {'ตังเม': '088-888-8888', 'เป่าหลง' : '099-999-9999', 'เจนนี่': '011-111-1111'}
len(name_to_phone_number) #--> 3

ในตัวอย่างนี้ ดิกชันนารี name_to_phone_number มีคู่คีย์-แวลูทั้งหมด 3 คู่ ดังนั้น len(name_to_phone_number) จะคืนค่า 3

เพิ่มข้อมูล (คู่ key-value) เข้าดิกชันนารี#

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

name_to_phone_number = {'ตังเม': '088-888-8888', 'เป่าหลง' : '099-999-9999', 'เจนนี่': '011-111-1111'}
name_to_phone_number['โนโซมิ'] = '066-666-6666'

บรรทัดแรกเราสร้างดิกชันนารีที่เก็บค่าคีย์แวลู ดังนี้

คีย์

แวลู

‘ตังเม’

‘088-888-8888’

‘เป่าหลง’

‘099-999-9999’

‘เจนนี่’

‘011-111-1111’

หลังจากรันบรรทัดที่ 2 ข้อมูลในดิกชันนารีจะกลายเป็น

คีย์

แวลู

‘ตังเม’

‘088-888-8888’

‘เป่าหลง’

‘099-999-9999’

‘เจนนี่’

‘011-111-1111’

‘โนโซมิ’

‘066-666-6666’

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

name_to_phone_number = {'ตังเม': '088-888-8888', 'เป่าหลง' : '099-999-9999', 'เจนนี่': '011-111-1111'}
name_to_phone_number['โนโซมิ'] = '066-666-6666'
name_to_phone_number['ตังเม'] = '011-222-3333'

หลังจากรันโค้ดนี้แล้ว ข้อมูลในดิกชันนารีจะเปลี่ยนไปเป็นดังนี้

คีย์

แวลู

‘ตังเม’

‘011-222-3333’

‘เป่าหลง’

‘099-999-9999’

‘เจนนี่’

‘011-111-1111’

‘โนโซมิ’

‘066-666-6666’

ดิกชันนารีมีคีย์แวลู อยู่ 4 คู่ เท่าเดิม name_to_phone_number['ตังเม'] = '011-222-3333' เปลี่ยนคู่คีย์แวลู ที่มี 'ตังเม' เป็นคีย์ให้เป็นค่าใหม่ที่ป้อนเข้าไป ให้สังเกตด้วยว่าดิกชันนารีไม่ได้ลบคู่คีย์แวลูเก่า และเติมคู่ใหม่เข้าไป ดิกชันนารีจะเขียนทับตำแหน่งเดิม

เข้าถึงแวลูด้วยคีย์: [] และ .get()#

เราสามารถเข้าถึงค่าที่ตรงกับคีย์ในดิกชันนารีได้โดยระบุคีย์ที่ต้องการ ตัวอย่างเช่น หากเราต้องการทราบหมายเลขโทรศัพท์ของ ‘ตังเม’ จากดิกชันนารีที่เก็บหมายเลขโทรศัพท์ สามารถทำได้ดังนี้

name_to_phone_number = {'ตังเม': '088-888-8888', 'เป่าหลง' : '099-999-9999', 'เจนนี่': '011-111-1111'}
name_to_phone_number['ตังเม'] # ---> '088-888-8888'

การเข้าถึงข้อมูลในดิกชันนารีในลักษณะนี้เรียกว่า การเปิดหาข้อมูลในดิกชันนารี (dictionary lookup) ซึ่งเป็นการดึงข้อมูลจากดิกชันนารีโดยไม่ต้องค้นหาจากแหล่งอื่น

ข้อควรระวังคือ หากค้นหาด้วยคีย์ที่ไม่มีในดิกชันนารี จะเกิดข้อผิดพลาด KeyError

>>> name_to_phone_number = {'ตังเม': '088-888-8888', 'เป่าหลง' : '099-999-9999', 'เจนนี่': '011-111-1111'}
>>> name_to_phone_number['โนโซมิ']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'โนโซมิ'

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

>>> name_to_phone_number['011-111-1111']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: '011-111-1111'

เพื่อป้องกันการเกิด KeyError เราสามารถใช้เมท็อด .get() เพื่อค้นหาคีย์ โดยสามารถระบุค่าที่จะคืนค่าได้ ในกรณีที่คีย์นั้นไม่มีอยู่ในดิกชันนารี

>>> word_to_freq = {'the': 50000, 'jump' : 200, 'Andy': 80}
>>> word_to_freq['Jack']
KeyError: 'Jack'
>>> word_to_freq.get('Jack')
None
>>> word_to_freq.get('Jack', 0)
0

บรรทัดที่สุดท้ายคือการเปิดหาคีย์ 'Jack' ในดิกชันนารี แต่ถ้าหากไม่เจอให้ คืนค่า 0 ทำให้เราไม่ต้องเจอกับ KeyError

การวนซ้ำบนดิกชันนารี#

เราสามารถใช้ for วนซ้ำไปบนคีย์ (iterate on keys) วนซ้ำไปบนแวลู (iterate on values) หรือวนซ้ำไปบนคู่คีย์แวลู (iterate on key-value pairs) ขึ้นอยู่กับการใช้งาน โดยเราจะใช้เมท็อด.keys(), .values(), .items() ตามลำดับ ซึ่งเมท็อดเหล่านี้จะคืนค่าประเภทสิ่งที่วนซ้ำได้ (iterable) ซึ่งหมายถึงข้อมูลที่สามารถวนซ้ำได้

วนซ้ำไปบนคีย์#

เราสามารถใช้ for ในการวนซ้ำไปบนดิกชันนารีได้โดยตรง ในการวนซ้ำแบบนี้ ระบบจะตีความว่าต้องการวนซ้ำไปบนคีย์แต่ละตัว (ไม่ใช่การวนซ้ำบนคู่คีย์แวลู)

ตัวอย่างการวนซ้ำไปบนคีย์

name_to_phone_number = {'ตังเม': '088-888-8888', 'เป่าหลง' : '099-999-9999', 'เจนนี่': '011-111-1111'}
name_to_phone_number['โนโซมิ'] = '066-666-6666'
name_to_phone_number['ตังเม'] = '011-222-3333'
for name in name_to_phone_number: 
	print(name)

ผลลัพธ์ที่ได้คือ

ตังเม
เป่าหลง
เจนนี่
โนโซมิ

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

หากต้องการให้โค้ดมีความกระจ่างขึ้นว่าเรากำลังวนซ้ำไปบนคีย์สามารถใช้เมท็อด.keys() เช่น

for name in name_to_phone_number.keys(): 
	print(name)

หากต้องการเก็บคีย์ทั้งหมดในลิสต์ สามารถใช้คำสั่ง list() เพื่อแปลงสิ่งที่วนซ้ำได้ให้เป็นลิสต์ ดังตัวอย่างต่อไปนี้

name_list = list(name_to_phone_number.keys())

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

วนซ้ำไปบน value#

ดิกชันนารีมีเมท็อด.values() ที่ทำให้เราวนซ้ำไปบนแวลูได้

name_to_phone_number = {'ตังเม': '088-888-8888', 'เป่าหลง' : '099-999-9999', 'เจนนี่': '011-111-1111'}
name_to_phone_number['โนโซมิ'] = '066-666-6666'
for number in name_to_phone_number.values():
	print(number)

ผลลัพธ์ที่ได้คือ

088-888-8888
099-999-9999
011-111-1111
066-666-6666

ลำดับที่ปรากฎเหมือนกันกับการวนซ้ำไปบนคีย์

เมท็อด.values() ให้ค่าเป็นสิ่งที่วนซ้ำได้ เช่นกัน เพราะฉะนั้นเราสามารถเก็บเอาแวลูทั้งหมดใส่ลิสต์โดยการแปลงประเภทข้อมูลให้เป็นลิสต์ด้วยคำสั่ง list()

phone_numbers = list(name_to_phone_number.values())

วนซ้ำไปบนคู่คีย์แวลู#

เราสามารถใช้เมท็อด.items() ในการวนซ้ำไปบนคู่คีย์แวลู ซึ่งเมท็อดนี้จะคืนค่าเป็นคู่คีย์แวลูที่เป็นสิ่งที่วนซ้ำได้ ตัวอย่างการใช้งานเมท็อดนี้ ดังต่อไปนี้

for name, number in name_to_phone_number.items():
	print(name)
	print(number)

ผลลัพธ์ที่ได้คือ

ตังเม
088-888-8888
เป่าหลง
099-999-9999
เจนนี่
011-111-1111
โนโซมิ
066-666-6666

ในตัวอย่างนี้ name และ number เป็นตัวแปรที่ใช้เก็บค่าคีย์และแวลูตามลำดับในแต่ละรอบของการวนซ้ำ เมท็อด .items() จะคืนค่าเป็นคู่ของคีย์และแวลูในรูปของทูเปิล เราจะเรียนรู้เกี่ยวกับทูเปิลในหัวข้อถัดไป

ตรวจสอบว่ามีคีย์นั้นอยู่ในดิกชันนารีหรือไม่: in#

เราสามารถใช้ตัวดำเนินการ in เพื่อตรวจสอบว่าคีย์ที่ต้องการมีอยู่ในดิกชันนารีหรือไม่ ตัวอย่างเช่น

word_to_freq = {'the': 50000, 'jump' : 200, 'Andy': 80}
'Jack' in word_to_freq #---> False

การตรวจสอบว่าแวลูมีอยู่ในดิกชันนารีหรือไม่ทำได้ยากกว่า เนื่องจากดิกชันนารีไม่ได้ออกแบบมาเพื่อการตรวจสอบแวลูโดยตรง เราจำเป็นต้องวนซ้ำไปบนแวลูทั้งหมดเพื่อหาแวลูที่ตรงกัน ตัวอย่างการใช้วงวน for เพื่อตรวจสอบว่าแวลู 200 มีอยู่ในดิกชันนารีหรือไม่

value_to_find = 200
word_to_freq = {'the': 50000, 'jump' : 200, 'Andy': 80}

def find_value(value_to_find, word_to_freq):
  for freq in word_to_freq.values():
    if value_to_find == freq:
      return True
  return False

find_value(value_to_find, word_to_freq) #---> True

อีกวิธีหนึ่งคือการแปลงแวลูทั้งหมดในดิกชันนารีให้เป็นลิสต์ แล้วใช้ตัวดำเนินการ in ในการตรวจสอบ

value_to_find = 200
word_to_freq = {'the': 50000, 'jump' : 200, 'Andy': 80}

def find_value(value_to_find, word_to_freq):
  freq_list = list(word_to_freq.values())
  return value_to_find in freq_list

find_value(value_to_find, word_to_freq) #---> True

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

ลบคู่คีย์แวลู: .pop#

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

word_to_freq = {'the': 50000, 'jump' : 200, 'Andy': 80}
word_to_freq.pop('the')
print(word_to_freq)

ผลลัพธ์ที่ได้คือ

{'jump': 200, 'Andy': 80}

ในกรณีที่คีย์ที่ระบุไม่มีอยู่ในดิกชันนารี เราสามารถระบุค่าเริ่มต้นในเมท็อด .pop() เพื่อป้องกันข้อผิดพลาด KeyError เช่น

word_to_freq = {'the': 50000, 'jump': 200, 'Andy': 80}
result = word_to_freq.pop('Jack', None)
print(result)  # ผลลัพธ์: None

ทูเปิล (tuple)#

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

first_last_name = ('อรรถพล', 'ธำรงรัตนฤทธิ์')

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

  • พิกัดบนแผนที่โลก อาจจะถูกเก็บในทูเปิลโดยที่ดัชนี 0 เป็น latitude และดัชนี 1 เป็น longtitude อาจจะถูกเก็บในทูเปิล ดังนี้ ('13.7563° N', '100.5018°')

  • ชื่อนามสกุล อาจจะถูกเก็บในทูเปิลโดยที่ดัชนี 0 เป็นชื่อดัชนี 1 เป็นนามสกุล ดังนี้ ('อรรถพล', 'ธำรงรัตนฤทธิ์')

  • สี มักจะถูกจัดเก็บเป็นค่าแดงเขียวน้ำเงิน (RGB) โดยที่ดัชนี 0 เป็นค่าของสีแดง ดัชนี 1 เป็นค่าของสีเขียว ดัชนี 2 เป็นค่าของสีน้ำเงิน ดังนี้ (256, 256, 256)

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

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

first_last_name = ('อรรถพล', 'ธำรงรัตนฤทธิ์')
first_name = first_last_name[0]
last_name = first_last_name[1]
print (first_name) # อรรถพล
print (last_name) # ธำรงรัตนฤทธิ์

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

first_last_name = ('อรรถพล', 'ธำรงรัตนฤทธิ์')
first_name, last_name = first_last_name
print (first_name) # อรรถพล
print (last_name) # ธำรงรัตนฤทธิ์

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

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

เคาน์เตอร์#

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

เคาน์เตอร์ไม่ใช่คลาสแบบบิวท์ (built-in class) ดังนั้นก่อนจะใช้จะต้อง import เข้ามาก่อนที่จะเรียกใช้ เคาน์เตอร์เป็นคลาสที่เป็นส่วนหนึ่งของโมดูล collections

การสร้างเคาน์เตอร์ว่าง ๆ#

ใช้คำสั่ง Counter() ในการสร้างเคาน์เตอร์ซึ่งเป็นคำสั่งที่ต้อง import เข้ามาก่อน

from collections import Counter
word_count = Counter()

การสร้างเคาน์เตอร์จากดิกชันนารี#

หากมีดิกชันนารีที่แวลูเป็นจำนวนอยู่แล้ว เราสามารถแปลงดิกชันนารีนั้นเป็นเคาน์เตอร์ได้โดยตรง

word_count = Counter({'b':1, 'a':3, 'c':1})

การสร้างเคาน์เตอร์จากลิสต์#

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

letter_list = ['a', 'b', 'c', 'a','b', 'a']
letter_count = Counter(letter_list)
letter_count['a'] #--> 3
letter_count['b'] #--> 2
letter_count['c'] #--> 1

คีย์ที่ไม่ได้อยู่ในเคาน์เตอร์แวลูจะเป็น 0#

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

letter_count['d'] #--> 0

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

word_counter = Counter()
for word in word_list:
    if word.isalpha():
        word_counter[word] = word_counter[word] + 1

word_counter[word] คืนค่ามาเป็น 0 หากคำนั้นไม่ได้ปรากฏในเคานเตอร์ซึ่งทำให้นิพจน์ ทางขวามือของเครื่องหมายเท่ากับเป็น 0 + 1 ซึ่งเป็นผลที่เราต้องการ ถ้าหากเราจะใช้ดิกชันนารีแทนเคาน์เตอร์ก็สามารถทำได้ ดังนี้

word_counter = {}
for word in word_list:
    if word.isalpha():
        if word not in word_counter:
            word_counter[word] = 0
        word_counter[word] = word_counter[word] + 1

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

หาค่าคีย์ที่คู่กับแวลูที่สูงที่สุด: .most_common()#

เคาน์เตอร์มีเมท็อด .most_common() ซึ่งใช้สำหรับหาคีย์ที่พบบ่อยที่สุด

letter_list = ['a', 'b', 'c', 'a','b', 'a']
letter_count = Counter(letter_list)
letter_count.most_common() #--> [('a', 3), ('b', 2), ('c', 1)]
letter_count.most_common(1) #--> [('a', 3)]

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

เซต (Set)#

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

  1. เซตไม่เก็บลำดับของข้อมูล เพราะฉะนั้นเราไม่สามารถใช้ตัวดำเนินการ [] ในการเข้าถึงข้อมูลได้ ไม่สามารถใช้ [] ในการหั่นเซตได้ เนื่องจากเซตไม่มีลำดับที่แน่นอน ถึงแม้จะสามารถใช้วงวนฟอร์เพื่อเข้าถึงสมาชิกในเซตได้ แต่ลำดับการเข้าถึงสมาชิกแต่ละตัวจะไม่ตรงกับลำดับที่เราเพิ่มสมาชิกเข้าไป

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

  3. การตรวจสอบว่าสมาชิกอยู่ในเซตทำได้รวดเร็ว เนื่องจากเซตไม่เก็บลำดับข้อมูล การตรวจสอบสมาชิกในเซตจึงทำได้เร็วกว่าการตรวจสอบในลิสต์ โดยอัลกอริทึมที่ใช้ในการตรวจสอบสมาชิกของเซตจะทำงานในเวลาคงที่ (เวลาที่ใช้ในการตรวจสอบสมาชิกภาพไม่ขึ้นอยู่กับขนาดของเซต)

การสร้างเซต และการหาขนาดของเซต#

เซตถูกสร้างขึ้นได้โดยการใช้ {} หรือคำสั่ง set() ถ้าต้องการสร้างเซตว่าง สามารถใช้ {} หรือคำสั่ง set() ได้เลย ดังตัวอย่างต่อไปนี้

basket = {}
basket = set()

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

basket = {'apple', 'orange', 'apple', 'orange', 'banana'}
basket # เก็บแค่ {'orange', 'banana', 'apple'}

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

basket = set(['apple', 'orange', 'apple', 'orange', 'banana'])
basket # เก็บแค่ {'orange', 'banana', 'apple'}

เพราะฉะนั้นเราสามารถใช้เซตในการเปลี่ยนสมาชิกในลิสต์ให้เหลือแต่สมาชิกที่ไม่ซ้ำกันได้ โดยการเปลี่ยนลิสต์ให้เป็นเซตด้วยคำสั่ง set และนำผลลัพท์ที่ได้เปลี่ยนกลับให้เป็นลิสต์ด้วยคำสั่ง list ดังตัวอย่างต่อไปนี้

fruits = ['apple', 'orange', 'apple', 'orange', 'banana']
fruit_set = set(fruits)
fruit_list_no_duplicate = list(fruit_set)

วิธีการหาขนาดของเซต (หรือจำนวนสมาชิกของเซต) คล้ายคลึงกับการหาขนาดของลิสต์ และการหาขนาดของดิกชันนารี เราใช้ len() ในการหาขนาดของเซตได้ ดังตัวอย่างต่อไปนี้

basket = {'apple', 'orange', 'apple', 'orange', 'banana'}
len(basket) # 3

การตรวจสอบการเป็นสมาชิกโดยใช้ in#

เราสามารถใช้ตัวดำเนินการ in เพื่อตรวจสอบว่าข้อมูลที่ได้มาเป็นสมาชิกของเซตหรือไม่ ตัวอย่างเช่น

languages = {'French', 'English', 'German', 'Chinese'}
'English' in languages # True
'Thai' in languages # False

เซตสละความสามารถในการเก็บลำดับของข้อมูล เพื่อแลกมากับความเร็วในการหาข้อมูล การตรวจสอบการเป็นสมาชิกในลิสต์ต้องไล่ตรวจสอบสมาชิกทีละตัวโดยใช้เวลาเชิงเส้น (linear time) ซึ่งหมายความว่าเวลาที่ใช้ในการค้นหาจะเพิ่มขึ้นตามจำนวนสมาชิกในลิสต์ ในทางตรงกันข้ามเซตใช้โครงสร้างข้อมูลแฮช (hashing) ซึ่งช่วยให้การค้นหาสมาชิกสามารถทำได้ในเวลาคงที่ (constant time) โดยไม่ขึ้นกับจำนวนสมาชิกที่อยู่ในเซต ทำให้เซตเหมาะสมกว่าลิสต์สำหรับการตรวจสอบการเป็นสมาชิก โดยเฉพาะเมื่อมีจำนวนข้อมูลมาก

การวนซ้ำบนเซต#

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

ตัวอย่าง#

languages = {'French', 'English', 'German'}
for language in languages:
    print(language)

ผลลัพธ์ที่ออกมาคือ

German
English
French

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

การเพิ่มสมาชิกในเซตด้วย add()#

เราสามารถเพิ่มสมาชิกในเซตได้โดยใช้คำสั่ง add ซึ่งต่างจากคำสั่ง append ของลิสต์ คำว่า append ภาษาอังกฤษแปลว่าเพิ่มไปตรงท้าย แต่คำว่า add แปลว่าเพิ่มเข้าไปโดยไม่ได้คำนึงถึงลำดับของสมาชิก ผู้พัฒนาภาษาไพทอนเลือกใช้คำว่า add เพื่อแสดงให้เห็นว่าเซตไม่เก็บลำดับของสมาชิก

ตัวอย่าง#

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

student_names = ['อาทิตยา', 'วิภา', 'อรรถพล', 'วิภา', 'คริส', 'สุชาดา']
aa_name_set = {} # จะใช้ aa_name_set = set() แทนก็ได้
for name in student_names:
    if name.endswith('า'):
        aa_name_set.add(name)

เอาสมาชิกออกจากเซตด้วย remove() และ discard()#

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

ตัวอย่าง#

สมมติว่าเรามีเซตของตัวเลือกที่เป็นไปได้ในแบบสอบถาม เช่น Strongly disagree, Disagree, Neutral, Agree, และ Strongly agree และเราต้องการลบตัวเลือก “Strongly disagree” และ “Strongly agree” ออกจากเซต

possible_responses = {'Strongly disagree', 'Disagree', 'Neutral', 'Agree', 'Strongly agree'}
possible_responses.remove('Strongly disagree')
possible_responses.remove('Strongly agree')

หากพยายามลบสมาชิกที่ไม่มีอยู่ในเซตด้วย remove() จะทำให้เกิดข้อผิดพลาด KeyError ดังตัวอย่างนี้

possible_responses.remove('Maybe')  # จะเกิด KeyError เพราะ 'Maybe' ไม่มีอยู่ในเซต

หากต้องการลบสมาชิกออกจากเซตโดยไม่ต้องตรวจสอบว่าสมาชิกนั้นมีอยู่หรือไม่ สามารถใช้เมท็อด discard() แทน remove() ซึ่งจะไม่ทำให้เกิดข้อผิดพลาด KeyError แม้สมาชิกนั้นไม่มีอยู่ในเซต

possible_responses.discard('Maybe')  # ไม่เกิดข้อผิดพลาดแม้ 'Maybe' ไม่มีอยู่ในเซต

discard() มีประโยชน์ในกรณีที่ไม่แน่ใจว่าสมาชิกที่ต้องการลบมีอยู่ในเซตหรือไม่ ช่วยให้โปรแกรมไม่เกิดข้อผิดพลาดและทำงานต่อไปได้อย่างราบรื่น

การดำเนินการของเซต: อินเตอร์เซกชัน ยูเนียน และผลต่างของเซต#

เซตในภาษาไพทอนรองรับการดำเนินการของเซตทุกตัว สมมติว่าเรามีเซต A เก็บอยู่ในตัวแปร setA และเซต B เก็บอยู่ในตัวแปร setB

  • อินเตอร์เซกชัน (A ∩ B) คือ การสร้างเซตที่ประกอบด้วยสมาชิกที่อยู่ทั้งในเซต A และเซต B ภาษาไพทอนใช้คำสั่ง setA.intersection(setB)

  • ยูเนียน (A ∪ B) คือ การสร้างเซตที่ประกอบด้วยสมาชิกที่อยู่ในเซต A หรือเซต B อย่างน้อยหนึ่งเซต ภาษาไพทอนใช้คำสั่ง setA.union(setB)

  • การลบกันของเซต (A - B) คือ การสร้างเซตที่ประกอบด้วยสมาชิกที่อยู่ในเซต A แต่ไม่อยู่ในเซต B ภาษาไพทอนใช้คำสั่ง setA - setB หรือ setA.difference(setB)

เซตในโลกของการประมวลผลภาษาธรรมชาติมักจะใช้ในการเก็บรายการคำศัพท์ (vocabulary หรือ vocab) เช่น คำศัพท์ที่ปรากฎทั้งหมดในข้อสอบชุดหนึ่ง คำศัพท์ที่เห็นทั้งหมดในคลังข้อมูล (corpus ชุดข้อมูลที่รวบรวมข้อมูลภาษาเอาไว้จำนวนมาก) คำศัพท์ที่แบบจำลองเข้าใจความหมายอยู่แล้ว เซตเหมาะแก่การเก็บรายการคำศํพท์ เพราะ รายการคำศัพท์มักประกอบด้วยคำศัพท์ที่พบเห็นอย่างน้อยหนึ่งครั้ง โดยไม่คำนึงว่าเกิดขึ้นซ้ำหรือไม่ และไม่คำนึงถึงลำดับการเกิดของคำศัพท์ นอกจากนั้นแล้วเซตในภาษาไพทอนยังรองรับการใช้การดำเนินการของเซตได้อย่างสะดวกสบายอีกด้วย ในตัวอย่างต่อไปนี้ เรามีคำศัพท์จากแบบจำลองภาษาขนาดใหญ่ที่ครอบคลุมหลายภาษา เก็บไว้ในตัวแปร multilingual_vocab และมีคำศัพท์จากแบบจำลองภาษาไทย เก็บไว้ในตัวแปร thai_vocab

ตัวอย่าง: อินเตอร์เซกชัน#

สมมติว่าเราต้องการหาว่าคำศัพท์ที่ปรากฎในทั้งสองเซต คือ multilingual_vocab และ thai_vocab สามารถใช้คำสั่ง intersection ในการหาคำศัพท์ที่ปรากฎในทั้งสองตัวแปรได้ดังนี้

multilingual_vocab = {'hello', 'สวัสดี', '你好', 'สวัสดี', 'สวัสดี', 'สวัสดี'}
thai_vocab = {'สวัสดี', 'ขอบคุณ'}
multilingual_vocab.intersection(thai_vocab) # {'สวัสดี'}

หลังจากรันโค้ดแล้วลอง print ค่าที่ปรากฎอยู่ในตัวแปรจะได้ผลดังนี้

print(multilingual_vocab)
print(thai_vocab)

เอาท์พุธที่ปรากฎบนหน้าจอ คือ

{'你好', 'สวัสดี', 'hello'}
{'ขอบคุณ', 'สวัสดี'}

คำสั่ง intersection คืนค่าเป็นเซตใหม่ขึ้นมาต่างหาก แยกต่างหากจาก multilingual_vocab และ thai_vocab ที่เราใช้ในการเรียกคำสั่ง หลังจากเรียกคำสั่ง set operation แล้ว สมาชิกของ multilingual_vocab และ thai_vocab จะไม่เปลี่ยนแปลง

เพราะฉะนั้นที่ถูกต้องคือ ต้องเก็บค่าที่คืนมาจากคำสั่งไว้ในตัวแปรใหม่

multilingual_vocab = {'hello', 'สวัสดี', '你好', 'สวัสดี', 'สวัสดี', 'สวัสดี'}
thai_vocab = {'สวัสดี', 'ขอบคุณ'}
overlapping_vocab = multilingual_vocab.intersection(thai_vocab) # {'สวัสดี'}

หากเราต้องการทราบจำนวนสมาชิกของแต่ละเซต สามารถใช้ len() ในการหาได้ ดังตัวอย่างต่อไปนี้

print(len(multilingual_vocab)) 
print(len(thai_vocab)) 
print(len(overlapping_vocab)) 

เอาท์พุธที่ปรากฎบนหน้าจอ คือ

4
2
1

ตัวอย่าง: ยูเนียน#

สมมติว่าเราต้องการขยายความสามารถของโมเดลหลากภาษา โดยการขยายคำศัพท์ ของโมเดลหลากภาษาให้มีคำศัพท์ที่มาจากแบบจำลองภาษาไทยด้วย สามารถใช้คำสั่ง union ในการขยายคำศัพท์ของโมเดลหลากภาษาได้ดังนี้

multilingual_vocab = {'hello', 'สวัสดี', '你好', 'สวัสดี', 'สวัสดี', 'สวัสดี'}
thai_vocab = {'สวัสดี', 'ขอบคุณ'}
larger_vocab = multilingual_vocab.union(thai_vocab) 
print(larger_vocab)

หลังจากรันโค้ดแล้วลอง print ค่าที่ปรากฎอยู่ในตัวแปรจะได้ผลดังนี้

{'สวัสดี', 'hello', 'ขอบคุณ', '你好'}

คำสั่ง union คืนค่าเป็นเซตใหม่ขึ้นมาต่างหาก แยกต่างหากจาก multilingual_vocab และ thai_vocab ที่เราใช้ในการเรียกคำสั่ง เช่นเดียวกับการใช้คำสั่ง intersection

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

multilingual_vocab = {'hello', 'สวัสดี', '你好', 'สวัสดี', 'สวัสดี', 'สวัสดี'}
thai_vocab = {'สวัสดี', 'ขอบคุณ'}
larger_vocab = multilingual_vocab.union(thai_vocab)
print(sorted(larger_vocab))

เอาท์พุตที่ปรากฎบนหน้าจอ คือ

['hello', 'ขอบคุณ', 'สวัสดี', '你好']

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

ตัวอย่าง: ผลต่างของเซต#

สมมติเราต้องการทราบว่า thai_vocab มีคำศัพท์ภาษาไทยคำใดบ้าง ที่ multilingual_vocab ไม่มี สามารถใช้คำสั่ง difference ในการหาคำศัพท์ได้ดังนี้

multilingual_vocab = {'hello', 'สวัสดี', '你好', 'สวัสดี', 'สวัสดี', 'สวัสดี'}
thai_vocab = {'สวัสดี', 'ขอบคุณ'}
thai_only_vocab = thai_vocab.difference(multilingual_vocab)
print(thai_only_vocab)

หลังจากรันโค้ดแล้วลอง print ค่าที่ปรากฎอยู่ในตัวแปรจะได้ผลดังนี้

{'ขอบคุณ'}

คำสั่ง difference คืนค่าเป็นเซตใหม่ขึ้นมาต่างหาก แยกต่างหากจาก multilingual_vocab และ thai_vocab ที่เราใช้ในการเรียกคำสั่ง เช่นเดียวกับการใช้คำสั่ง intersection และ union

สรุปเรื่องเซต#

เซตในภาษาไพทอนเป็นโครงสร้างข้อมูลที่เหมาะสมกับการเก็บข้อมูลที่ไม่มีการจัดลำดับ และไม่มีการเก็บข้อมูลที่ซ้ำกัน โดยเซตในภาษาไพทอนมีความสามารถในการใช้ set operation ได้ทั้งหมด 3 ตัว คือ intersection union และการลบกันของเซต ในการประมวลผลภาษาธรรมชาติเซตมักถูกใช้ในการเก็บรายการคำศัพท์ หรือเรียกว่า vocabulary

คำสั่งของเซตในภาษาไพทอน สรุปเป็นตารางได้ดังนี้

operation

สัญลักษณ์ทางคณิตศาสตร์

คำสั่งในไพทอน

intersection

A ∩ B

setA.intersection(setB)

union

A ∪ B

setA.union(setB)

difference

A - B

setA - setB หรือ setA.difference(setB)

ทั้งสามคำสั่งจะคืนค่าออกมาเป็นเซตใหม่แยกต่างหากจาก setA และ setB ที่เราใช้ในการเรียกคำสั่ง หลังจากเรียกคำสั่ง set operation แล้ว สมาชิกของ setA และ setB จะไม่เปลี่ยนแปลง

คอมพริเฮนชัน (Comprehension)#

ก่อนหน้านี้เราได้เรียนวิธีการสร้างลิสต์ ดิกต์ และเซ็ตด้วยวิธีการดังนี้

my_list = list()
my_list2 = []
my_dict = dict()
my_dict2 = {}
my_set = set()
my_set = set(my_list)

ลิสต์คอมพริเฮนชัน (List comprehension)#

ลิสต์คอมพริเฮนชัน (List comprehension) คือสร้างลิสต์ขึ้นมาจากสิ่งที่เป็น iterable เช่น ลิสต์ ดิกชันนารี และเซ็ต ซึ่งเป็นกรณีการใช้ที่เกิดขึ้นบ่อย เนื่องจากมีหลายครั้งที่เราต้องแปลงจากลิสต์เป็นอีกลิสต์หนึ่งซึ่งสมาชิกข้างในถูกแปลงให้เป็นอย่างหนึ่ง

ตัวอย่าง#

สมมติว่าเรามีลิสต์ของสตริง แต่เราต้องการเปลี่ยนให้เป็นตัวใหญ่ทั้งหมด เราสามารถสร้างลิสต์ใหม่เตรียมไว้รับ output จากนั้นทำการวนลูปบนสมาชิกของลิสต์ แปลงเป็นตัวใหญ่ จากนั้นก็ .append เข้าสู่ลิสต์ที่จะเป็น output ดังนี้

sentence = ['programming', 'is', 'very', 'interesting']
new_sentence = []
for w in sentence:
    new_sentence.append(w.upper())
print(new_sentence)
#ผลลัพธ์ : ['PROGRAMMING', 'IS', 'VERY', 'INTERESTING']

เราสามารถเขียนให้กระชับได้โดยการใช้ list comprehension ดังนี้

sentence = ['programming', 'is', 'very', 'interesting']
new_sentence2 =[w.upper() for w in sentence]
print(new_sentence2)
#ผลลัพธ์ : ['PROGRAMMING', 'IS', 'VERY', 'INTERESTING']

ตัวอย่าง#

สมมติว่าเราต้องการเปลี่ยนให้จากลิสต์เดิมให้เป็นลิสต์ที่มีความยาวของสตริงแต่ละตัวแทน เราสามารถทำด้วยวิธีที่คล้ายคลึงกัน

sentence = ['programming', 'is', 'very', 'interesting']
length_list = []
for w in sentence:
    length_list.append(len(w))
print (length_list)
#ผลลัพธ์ : [11, 2, 4, 11]

เราสามารถเขียนให้กระชับได้โดยการใช้ list comprehension ดังนี้

sentence = ['programming', 'is', 'very', 'interesting']
length_list = [len(w) for w in sentence] 
print(length_list)
#ผลลัพธ์ : [11, 2, 4, 11]

วิธีการใช้ list comprehension มีดังนี้ [expression for ตัวแปร in iterable] เช่น

  • [w.upper() for w in sentence]

  • [len(w) for w in sentence]

นิพจน์ (expression) คือสูตรหรือคำสั่งที่ใช้แปลงไอเท็มแต่ละไอเท็มในลิสต์ เซต หรือดิกชันนารี ให้กลายเป็นสมาชิกของเซตที่เรากำลังสร้างขึ้น

สิ่งที่วนซ้ำได้ (iterable) คือข้อมูลที่สามารถวนซ้ำได้ เช่น ลิสต์ เซต หรือดิกชันนารี

คอมพริเฮนชันแบบมีเงื่อนไข#

เราสามารถระบุเพิ่มเติมได้ด้วยว่าสมาชิกตัวไหนบ้างใน iterable ที่เป็น input จะถูกรวมไว้ในลิสต์ output

ตัวอย่าง#

สมมติว่าเราอยากแปลงลิสต์ที่มีคำในประโยคมาเป็นลิสต์ที่มีความยาวของแต่ละคำในประโยคอยู่ โดยที่เราสนใจเฉพาะคำที่มีความยาวมากกว่า 2 ตัวอักษร

sentence = ['programming', 'is', 'very', 'interesting']
length_list = []
for w in sentence:
    if (len(w) > 2):
        length_list.append(len(w))
print (length_list)
#ผลลัพธ์ : [11, 4, 11]

เราสามารถเขียนโค้ดให้กระชับขึ้นโดยใช้ list comprehension ได้ดังนี้

sentence = ['programming', 'is', 'very', 'interesting']
length_list = [len(w) for w in sentence if len(w) >2]
print(length_list)
#ผลลัพธ์ : [11, 4, 11]

ตัวอย่าง#

สมมติว่าเรามีลิสต์ของคำในประโยคอยู่ แต่ว่าเราอยากได้ลิสต์ที่มีคำที่เป็นตัวอักษรภาษาไทยเท่านั้น

import re
word_list = ['บท', 'นี้', 'เกี่ยวกับ', 'list', 'comprehension', 'และ', 'อื่นๆ']
new_word_list = []
for word in word_list:
    if re.match('[ก-์]+', word):
        new_word_list.append(word)
print(new_word_list)
#ผลลัพธ์ : ['บท', 'นี้', 'เกี่ยวกับ', 'และ', 'อื่นๆ']

เราสามารถเขียนโค้ดให้กระชับขึ้นโดยใช้ list comprehension ได้ดังนี้

import re
word_list = ['บท', 'นี้', 'เกี่ยวกับ', 'list', 'comprehension', 'และ', 'อื่นๆ']
new_word_list = [word for word in word_list if re.match('[ก-์]+', word)]
print(new_word_list)
#ผลลัพธ์ : ['บท', 'นี้', 'เกี่ยวกับ', 'และ', 'อื่นๆ']

สรุปการใช้คอมพริเฮนชันแบบมีเงื่อนไข#

วิธีการใช้ list comprehension แบบมีเงื่อนไข มีดังนี้ [expression for ตัวแปร in iterable if เงื่อนไข] เช่น

  • [len(w) for w in sentence if len(w) >2]

  • [word for word in word_list if re.match('[ก-์]+', word)]

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

เซตคอมพริเฮนชัน (Set comprihension)#

หลักการและวิธีการใช้ set comprehension แทบจะเหมือนกันกับ list comprehension เป็นการสร้างเซ็ตขึ้นมาจาก iterable อีกอันหนึ่งโดยอาจมีแปลงค่าของสมาชิกด้วยฟังก์ชันต่าง ๆ และ/หรือกรองเอาสมาชิกบางตัวออกโดยการกำหนดเงื่อนไขบูลีน

วิธีการใช้ set comprehension เหมือน list comprehension เพียงแค่เปลี่ยนเครื่องหมาย [] เป็น {}

{expression for ตัวแปร in iterable if เงื่อนไข}

ตัวอย่าง#

Vocabulary หมายถึงเซ็ตของคำศัพท์ทั้งหมดที่เราต้องการวิเคราะห์ หรือนำมาเป็นส่วนหนึ่งของโมเดล จะนับเฉพาะคำที่รูปไม่ซ้ำกัน

สมมติว่าเรามีลิสต์ของคำทั้งหมดที่มีอยู่ในคลังข้อมูล เราต้องการทราบ vocabulary ของคลังข้อมูลนี้ว่ามีคำว่าอะไรบ้าง โดยนับเฉพาะคำที่เป็นตัวภาษาอังกฤษเท่านั้น

word_list = ['baby', 'baby', 'baby', 'oh', '...', 'baby', 'baby', 'no','!']
vocab = set()
for word in word_list:
    if word.isalpha():
        vocab.add(word)
print(vocab)
#ผลลัพธ์ : {'baby', 'oh', 'no'}

เราสามารถเขียนโค้ดให้กระชับขึ้นโดยใช้ set comprehension ได้ดังนี้

word_list = ['baby', 'baby', 'baby', 'oh', '...', 'baby', 'baby', 'no','!']
vocab2 = {word for word in word_list if word.isalpha()}
print(vocab2)
#ผลลัพธ์ : {'baby', 'oh', 'no'}

ตัวอย่าง#

สมมติว่าเราต้องการสร้างเซตของชื่อนักเรียนที่ลงท้ายด้วยสระอาจากลิสต์รายชื่อนักเรียน

student_names = ['อาทิตยา', 'วิภา', 'อรรถพล', 'วิภา', 'คริส', 'สุชาดา']
aa_name_set = {name for name in student_names if name.endswith('า')}

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

  • นิพจน์ ไม่มีการเปลี่ยนแปลงชื่อนักเรียน แต่เราใช้ name ในการเข้าถึงชื่อนักเรียนแต่ละคน

  • สมาชิกเป็นชื่อที่ได้จากการใช้ for ในการวนซ้ำเพื่อเข้าถึงชื่อนักเรียนทุกคนในลิสต์ student_names ซึ่งเป็นสิ่งที่วนซ้ำได้

  • เงื่อนไขการเข้าสู่เซต เราใช้ name.endswith('า') ในการตรวจสอบว่าชื่อนักเรียนลงท้ายด้วยสระอาหรือไม่ ถ้าลงท้ายด้วยสระอา ชื่อนักเรียนจะถูกเพิ่มเข้าไปในเซตที่กำลังสร้างขึ้น แต่ถ้าไม่ลงท้ายด้วยสระอา ชื่อนักเรียนจะไม่ถูกเพิ่มเข้าไปในเซตที่กำลังสร้างขึ้น

ดิกต์คอมพริเฮนชัน (Dict comprehension)#

หลักการใช้ dict comprehension ก็คล้ายคลึงกันกับ list comprehension และ set comprehension เพียงแต่ว่าเราต้องกำหนดว่า key และ value ของดิกต์ที่เราต้องการสร้างขึ้นจะต้องเป็นอะไรบ้าง

วิธีการใช้ dict comprehension มีดังนี้

{expression-key:expression-value for ตัวแปร in iterable if เงื่อนไข}

ตัวอย่าง#

สมมติว่าเรามีรายชื่อของนักเรียนอยู่ในลิสต์ และเราต้องการสร้างดิกต์ที่ key เป็นชื่อนักเรียน และ value เป็นหมายเลขกลุ่ม 1-3 เพื่อที่จะจัดนักเรียนออกเป็น 3 กลุ่ม

from random import randint
students = ['Panyut', 'Mild', 'Bew', 'Fluke', 'Paolong', 'Tangmay', 'Ya']
student_to_group = {}
for student in students:
    student_to_group[student] = randint(1,3)
print(student_to_group)
#ผลลัพธ์ : {'Panyut': 2, 'Mild': 3, 'Bew': 1, 'Fluke': 2, 'Paolong': 2, 'Tangmay': 2, 'Ya': 1}

เราสามารถเขียนโปรแกรมข้างบนให้กระชับขึ้นโดยใช้ dict comprehension ได้

students = ['Panyut', 'Mild', 'Bew', 'Fluke', 'Paolong', 'Tangmay', 'Ya']
student_to_group = {student:randint(1,3) for student in students}
print(student_to_group)
#ผลลัพธ์ : {'Panyut': 2, 'Mild': 3, 'Bew': 1, 'Fluke': 2, 'Paolong': 2, 'Tangmay': 2, 'Ya': 1}

ตัวอย่าง#

ในการสร้างโมเดลเกี่ยวกับภาษา ขั้นตอนที่จะต้องเจอบ่อย ๆ คือการแทนคำด้วยหมายเลขประจำตัว (id หรือ index) ซึ่งเราต้องกำหนดให้แต่ละคำใน vocabulary นั้นไม่ซ้ำกัน

สมมติว่าเรามีลิสต์ของคำทั้งหมดในคลังข้อมูล เราต้องการสร้างดิกที่ key เป็นคำ และ value เป็นหมายเลขประจำตัวของคำคำนั้น

word_list = ['baby', 'baby', 'baby', 'oh', '...', 'baby', 'baby', 'no','!']
word_to_index = {}
for word in word_list:
    if word not in word_to_index:
        word_to_index[word] = len(word_to_index)
print(word_to_index)
#ผลลัพธ์ : {'baby': 0, 'oh': 1, '...': 2, 'no': 3, '!': 4}

เราสามารถเขียนโดย dict comprehension ให้กระชับขึ้นดังนี้

word_list = ['baby', 'baby', 'baby', 'oh', '...', 'baby', 'baby', 'no','!']
word_set = set(word_list)
word_list = list(word_set) # ทำให้เหลือเฉพาะคำที่ไม่ซ้ำ
word_to_index = {word:index for index, word in enumerate(word_list)}
print(word_to_index)
#ผลลัพธ์ : {'...': 0, 'baby': 1, 'oh': 2, '!': 3, 'no': 4}

สรุปข้อดี ข้อเสียของการใช้คอมพริเฮนชัน#

ข้อดีของการใช้ comprehension คือ

  • โค้ดสั้นกระชับสวยงาม ทำให้มีโอกาสเขียนโค้ดผิดน้อยลง เมื่อเทียบกับการใช้ for loop ในการแก้ปัญหาเดียวกัน

  • เร็วกว่าการใช้ for loop มาก เนื่องจากไพทอนจะทำการ optimize โค้ดให้สำหรับ comprehension

ข้อเสียของการใช้ comprehension คือ

  • ผู้ที่ไม่ได้คล่องการเขียนโค้ดด้วยภาษาไพทอนอาจจะอ่านไม่เข้าใจ เนื่องจากไม่มีภาษาคอมพิวเตอร์อื่นที่มีหรือนิยมใช้ comprehension

  • ถ้า expression ที่อยู่ใน comprehension ซับซ้อน จะทำให้อ่านได้ยาก ในกรณีนี้เราอาจจะเลี่ยงไปใช้ for loop แทน

สรุป#

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