บทนี้นำเสนอชนิดข้อมูลสำเร็จที่มีอยู่ในตัวอีกชนิดหนึ่ง ซึ่งคือ ทูเพิล พร้อมแสดงวิธีการใช้ ลิสต์ ดิกชันนารี และทูเพิล ทำงานด้วยกัน นอกจากนั้น เรายังอภิปรายกันถึง คุณลักษณะที่มีประโยชน์ของอาร์กิวเมนต์ความยาวแปรผัน พร้อมตัวดำเนินการการรวบรวม และ การกระจาย
เรื่องหนึ่ง: ยังไม่มีข้อตกลงอย่างเป็นทางการว่า ควรจะอ่าน “tuple” อย่างไร. บางคนอ่าน “ทับ-เพิล” ที่คล้องกับ “supple” แต่ในบริบทของการเขียนโปรแกรม คนส่วนใหญ่อ่าน “ทู-เพิล” ที่คล้องกับ “quadruple”
ทูเพิลเป็นลำดับของค่าต่างๆ เป็นข้อมูลชนิดใดก็ได้ และ ค่าต่างๆ เหล่านั้นอ้างอิงได้ด้วยดัชนีที่เป็นเลขจำนวนเต็ม ซึ่งในแง่นี้ ทูเพิลจะคล้ายๆ กับลิสต์ ความต่างที่สำคัญ คือ ทูเพิลไม่สามารถเปลี่ยนแปลงแก้ไขได้
ในแง่ไวยากรณ์ ทูเพิล เป็นลำดับของค่าต่างๆ ที่คั่นระหว่างกันด้วยเครื่องหมายจุลภาค (‘,’) เช่น
>>> t = 'a', 'b', 'c', 'd', 'e'
นอกจากนั้น ทูเพิลมักจะอยู่ระหว่างเครื่องหมายวงเล็บ ซึ่งจริงๆ แล้ว มันไม่ได้จำเป็นเลย ที่จะต้องใช้เครื่องหมายวงเล็บ เช่น
>>> t = ('a', 'b', 'c', 'd', 'e')
เพื่อสร้างทูเพิลที่มีอิลิเมนต์เดียว เราจำเป็นถ้าใส่เครื่องจุลภาคต่อท้ายเข้าไป เช่น
>>> t1 = 'a', >>> type(t1) <class 'tuple'>
ค่าในวงเล็บไม่ใช่ทูเพิล:
>>> t2 = ('a') >>> type(t2) <class 'str'>
อีกวิธีที่จะสร้างทูเพิล คือ การใช้ฟังก์ชันสำเร็จที่มีอยู่ในตัว tuple
ถ้าไม่ใส่อาร์กิวเมนต์ มันจะสร้างทูเพิลว่างให้:
>>> t = tuple() >>> t ()
ถ้าอาร์กิวเมนต์ของฟังก์ชัน เป็นลำดับ (ไม่ว่าจะเป็น สายอักขระ ลิสต์ หรือทูเพิล) ผลที่ได้จะเป็น ทูเพิลของอิลิเมนต์ของลำดับนั้น:
>>> t = tuple('lupins') >>> t ('l', 'u', 'p', 'i', 'n', 's')
เพราะว่า tuple
เป็นชื่อของฟังก์ชันสำเร็จรูป เราจึงไม่ควรใช้มันเป็นชื่อตัวแปร
ตัวดำเนินการต่างๆ ที่ใช้กับลิสต์ได้ ส่วนใหญ่ก็ใช้กับทูเพิลได้ ตัวดำเนินการวงเล็บสี่เหลี่ยมเลือกอิลิเมนต์ตามดัชนี:
>>> t = ('a', 'b', 'c', 'd', 'e') >>> t[0] 'a'
และตัวดำเนินการตัด (slice operator) เลือกขอบเขตของอิลิเมนต์ เช่น
>>> t[1:3] ('b', 'c')
แต่ถ้าเราลองแก้ไขค่าอิลิเมนต์ของทูเพิล เราจะเห็นข้อผิดพลาดแจ้งออกมา:
>>> t[0] = 'A' TypeError: object doesn't support item assignment
เพราะว่า ทูเพิลไม่สามารถเปลี่ยนแปลงได้ (immutable) เราจึงไม่สามารถที่จะแก้ไข หรือเปลี่ยนแปลงอิลิเมนต์ของทูเพิลได้ แต่เราสามารถแทนที่ทูเพิล ด้วยทูเพิลใหม่ได้:
>>> t = ('A',) + t[1:] >>> t ('A', 'b', 'c', 'd', 'e')
คำสั่งนี้สร้างทูเพิลใหม่ขึ้นมา และกำหนดให้ตัวแปร t
ไปอ้างถึงมัน
ตัวดำเนินการเชิงสัมพันธ์ทำงานกับทูเพิลและข้อมูลแบบลำดับชนิดอื่นๆ ได้ ไพธอนทำงานโดย เริ่มจากเปรียบเทียบอิลิเมนต์แรกจากแต่ละทูเพิล ถ้าอิลิเมนต์จากสองทูเพิลเท่ากัน ไพธอนจะไม่สนใจ และขยับไปตรวจอิลิเมนต์ในลำดับถัดไปจาอทั้งสองทูเพิล ถ้ามันต่างกัน ไพธอนจะทำงานเปรียบเทียบ และใช้ผลการเปรียบเทียบของอิลิเมนต์คู่นี้ เป็นผลของตัวดำเนินการ โดยอิลิเมนต์ที่เหลือจะไม่นำมาคิดเลย
>>> (0, 1, 2) < (0, 3, 4) True >>> (0, 1, 2000000) < (0, 3, 4) True >>> (0, 1, 1, 8, 10, 100) < (0, 1, 1, 9, 1, 0) True >>> (0, 1, 1, 11, 10, 100) < (0, 1, 1, 9, 1, 0) False
คำสั่งแรก คู่แรกที่ต่างกันคือดัชนีที่ 1 และ อิลิเมนต์ในทูเพิลแรกมีค่าน้อยกว่าอิลิเมนต์ในทูเพิลสอง (ค่า 1 เปรียบเทียบกับ 3) จึงให้ผลออกมาเป็น True
คำสั่งสอง ก็เช่นเดียวกัน เมื่อได้ผลจากคู่ดัชนีที่ 1 (ค่า 1 เปรียบเทียบกับ 3) แล้ว คู่ที่เหลือจะไม่ถูกคำมาคิด นั่นคือ คู่ดัชนี 2 (ค่า 2000000 เปรียบเทียบกับ 4) ไม่ได้ถูกนำมาคิด คำสั่งสาม คู่แรกที่ต่างกันคือดัชนีที่ 3 (เปรียบเทียบ ค่า 8 กับ 9) ผลของการเปรียบเทียบคู่นี้คือ ผลลัพธ์ คู่ที่เหลือจะไม่ถูกคำมาคิด คำสั่งสี่ ก็เช่นเดียวกัน เมื่อได้ผลจากคู่ดัชนี 2 (ค่า 11 เปรียบเทียบกับ 9) แล้ว คู่ที่เหลือจะไม่ถูกคำมาคิด
บางครั้ง เราอาจต้องการสลับค่าระหว่างตัวแปรสองตัว ด้วยวิธีการกำหนดค่าแบบทั่วๆ ไป เราต้องใช้ตัวแปรชั่วคราวเข้ามาช่วย ตัวอย่างเช่น สลับค่าระหว่างตัวแปร a
และตัวแปร b
:
>>> temp = a >>> a = b >>> b = temp
วิธีนี้ค่อนข้างงุ่มง่าม วิธีการกำหนดค่าทูเพิล (tuple assignment) ดูสละสลวยกว่าเยอะ:
>>> a, b = b, a
ฝั่งซ้ายมือเป็นทูเพิลของตัวแปร ส่วนฝั่งขวามือเป็นทูเพิลของนิพจน์ แต่ละค่าจะถูกกำหนดไปให้กับตัวแปรตามลำดับ นิพจน์ทั้งหมดทางขวามือจะถูกประเมินค่า ก่อนดำเนินการกำหนดค่า
จำนวนตัวแปรทางซ้ายมือ และจำนวนของค่าต่างๆ ทางขวามือ จะต้องเท่ากัน:
>>> a, b = 1, 2, 3 ValueError: too many values to unpack
จริงๆ แล้ว ทางขวามือจะเป็นข้อมูลลำดับชนิดใดๆ ก็ได้ (สายอักขระ ลิสต์ หรือทูเพิล) ตัวอย่าง เช่น เพื่อจะแยกอีเมล์แอดเดรส (email address) ออกมาเป็น ชื่อผู้ใช้ และโดเมน (domain) เราอาจจะเขียน:
>>> addr = 'monty@python.org' >>> uname, domain = addr.split('@')
ค่าที่คืนออกมาจาก split
เป็นลิสต์ของสองอิลิเมนต์ โดย อิลิเมนต์แรกจะถูกกำหนดให้กับ uname
และอิลิเมนต์ที่สองให้กับ domain
>>> uname 'monty' >>> domain 'python.org'
ถ้าพูดกันตรงๆ ฟังก์ชันสามารถคืนค่าออกมาได้ค่าเดียว แต่ถ้าค่าที่คืนมาเป็นทูเพิล ผลของมันจึงเหมือนกับการคืนค่าออกมาหลายๆ ค่า ตัวอย่างเช่น ถ้าเราต้องการหารเลขจำนวนเต็ม และคำนวณทั้ง ผลหาร (quotient) และเศษ (remainder) มันก็ไม่ค่อยมีประสิทธิภาพ ถ้าต้องคำนวณ x/y
แล้วก็ x%y
มันดีกว่าที่จะคำนวณทั้งสองอย่างพร้อมๆ กัน
ฟังก์ชันสำเร็จรูป divmod
รับอาร์กิวเมนต์สองตัว และให้ทูเพิลที่มีสองค่าออกมา ซึ่งคือ ผลหารและเศษ เราสามารถเก็บค่าผลลัพธ์นี้เป็นทูเพิลได้:
>>> t = divmod(7, 3) >>> t (2, 1)
หรือ ใช้การกำหนดค่าทูเพิล เพื่อเก็บแต่ละอิลิเมนต์แยกกัน:
>>> quot, rem = divmod(7, 3) >>> quot 2 >>> rem 1
นี่เป็นตัวอย่างของฟังก์ชันที่คืนค่าทูเพิลออกมา:
def min_max(t): return min(t), max(t)
max
และ min
เป็นฟังก์ชันสำเร็จรูป ที่ใช้หาค่ามากที่สุดและน้อยที่สุด ในข้อมูลลำดับ min_max
คำนวณค่าทั้งสอง และให้ทูเพิลของค่าทั้งสองออกมา
เราสามารถสร้างฟังก์ชันที่รับอาร์กิวเมนต์ที่มีจำนวนแปรเปลี่ยนได้. ชื่อพารามิเตอร์ที่ขึ้นต้นด้วย *
จะ รวบรวม (gather) อาร์กิวเมนต์หลายๆ ตัวเข้าเป็นทูเพิล ตัวอย่างเช่น printall
รับอาร์กิวเมนต์กี่ตัวก็ได้ และก็พิมพ์ค่าเหล่านั้นออกมา:
def printall(*args): print(args)
พารามิเตอร์รวม args
สามารถจะตั้งเป็นชื่ออะไรก็ได้ แต่มักจะนิยมตั้งเป็นชื่อ args
. ฟังก์ชันทำงานดังนี้:
>>> printall(1, 2.0, '3') (1, 2.0, '3')
ตรงข้ามกับการรวบรวมคือ การแยกกระจาย (scatter) ถ้าเรามีค่าหลายๆ ค่า และต้องการส่งเข้าไปให้ฟังก์ชันผ่านอาร์กิวเมนต์หลายๆ ตัว เราก็สามารถใช้ตัวดำเนินการ *
ได้. ตัวอย่าง divmod
รับอาร์กิวเมนต์สองตัว ต้องการสองตัว และสองตัวเท่านั้น ไม่ได้ใช้ทูเพิล:
>>> t = (7, 3) >>> divmod(t) TypeError: divmod expected 2 arguments, got 1
แต่เราสามารถแยกกระจายทูเพิลได้:
>>> divmod(*t) (2, 1)
ฟังก์ชันสำเร็จรูปหลายๆ อันใช้ทูเพิลเป็นอาร์กิวเมนต์ความยาวแปรผัน ตัวอย่างเช่น max
และ min
สามารถรับอาร์กิวเมนต์กี่ตัวก็ได้:
>>> max(1, 2, 3) 3
แต่ sum
ทำไม่ได้
>>> sum(1, 2, 3) TypeError: sum expected at most 2 arguments, got 3
เพื่อเป็นแบบฝึกหัด ให้ลองเขียนฟังก์ชัน ชื่อว่า sumall
ที่รับอาร์กิวเมนต์กี่ตัวก็ได้ และรีเทิร์นค่าผลรวมออกมา
zip
เป็นฟังก์ชันสำเร็จรูป ที่รับข้อมูลลำดับตั้งแต่สองชุดขึ้นไป และรีเทิร์นลิสต์ของทูเพิลออกมา โดยที่ แต่ละทูเพิล จะมีอิลิเมนต์มาจากแต่ละชุดข้อมูลลำดับ ชื่อของฟังก์ชันมาจาก ซิป(ซิปกางเกง) ที่รูดแล้ว มันจะขบฟันของซิปจากสองฝั่งเข้าด้วยกัน
ตัวอย่างนี้ซิปข้อมูลสายอักขระและลิสต์เข้าด้วยกัน
>>> s = 'abc' >>> t = [0, 1, 2] >>> zip(s, t) <zip object at 0x7f7d0a9e7c48>
ผลลัพธ์จากฟังก์ชัน zip
จะเป็น ซิปออบเจ๊คต์ (zip object) ที่สามารถวนเข้าถึงแต่ละคู่ทูเพิลได้ วิธีที่นิยมใช้กับ zip
ที่สุดคือ ใช้กับลูป for
เช่น:
>>> for pair in zip(s, t): ... print(pair) ... ('a', 0) ('b', 1) ('c', 2)
ซิปออบเจ๊คต์ เป็น ตัววนซ้ำ (iterator) ซึ่งเป็นออบเจ๊คต์ที่สามารถทำวนซ้ำกับลำดับได้ ตัววนซ้ำ จะคล้ายๆ กับลิสต์ ในหลายๆ แง่ แต่ต่างจากลิสต์ คือ เราไม่สามารถใช้ดัชนีเลือกอิลิเมนต์ออกมาจากตัววนซ้ำได้
ถ้าต้องการใช้ตัวดำเนินการของลิสต์จริงๆ เราก็สามารถแปลงซิปออปเจ๊คต์ไปเป็นลิสต์ได้:
>>> list(zip(s, t)) [('a', 0), ('b', 1), ('c', 2)]
ผลลัพธ์จะเป็นลิสต์ของทูเพิล ในตัวอย่างนี้ แต่ละทูเพิลจะมีอักขระจากสายอักขระ s
และอิลิเมนต์จากลิสต์ t
ถ้าลำดับข้อมูลมีความยาวไม่เท่ากัน ผลลัพธ์จะมีความยาวตามลำดับที่สั้นกว่า
>>> list(zip('Anne', 'Elk')) [('A', 'E'), ('n', 'l'), ('n', 'k')]
เราสามารถใช้การกำหนดค่าทูเพิลกับ for
ลูป เพื่อท่องสำรวจลิสต์ของทูเพิลได้:
t = [('a', 0), ('b', 1), ('c', 2)] for letter, number in t: print(number, letter)
แต่ละครั้งที่ทำลูป ไพธอนเลือกทูเพิลต่อไปในลิสต์ และกำหนดค่าอิลิเมนต์ให้ letter
และ number
ผลลัพธ์ของลูปนี้คือ:
0 a 1 b 2 c
ถ้าเรารวม zip
, for
, และการกำหนดค่าทูเพิล เราจะได้เทคนิค ที่สามารถท่องสำรวจลำดับข้อมูลสองลำดับ (หรือมากกว่า) ได้พร้อมๆ กัน ตัวอย่าง has_match
รับสองลำดับข้อมูล t1
และ t2
และคืนค่า True
ถ้ามีดัชนี i
ที่ t1[i] == t2[i]
:
def has_match(t1, t2): for x, y in zip(t1, t2): if x == y: return True return False
ถ้าเราอยากท่องสำรวจอิลิเมนต์ต่างๆ ในลำดับข้อมูล พร้อมดัชนีด้วย เราสามารถใช้ฟังก์ชันสำเร็จรูป enumerate
:
for index, element in enumerate('abc'): print(index, element)
ผลลัพธ์จาก enumerate
เป็นอีนูเมอร์เรตออปเจ๊คต์ (enumerate object) ที่สามารถทำวนซ้ำลำดับคู่ โดย แต่ละคู่จะมี ดัชนี(เริ่มด้วย 0) และอิลิเมนต์จากลำดับข้อมูล ตัวอย่างนี้ จะเห็นผลลัพธ์เป็น
0 a 1 b 2 c
ดิกชันนารีมีเมธอด ชื่อว่า items
ที่รีเทิร์นข้อมูลลำดับของทูเพิลออกมา โดยที่แต่ละทูเพิล คือ คู่กุญแจค่า ในดิกชันนารี
>>> d = {'a':0, 'b':1, 'c':2} >>> t = d.items() >>> t dict_items([('c', 2), ('a', 0), ('b', 1)])
ผลลัพธ์คือ ออปเจ๊คต์ dict_items
ที่เป็นตัววนซ้ำ สามารถท่องสำรวจคู่กุญแจค่าได้ เราสามารถใช้มันใน for
ลูปได้ดังนี้:
>>> for key, value in d.items(): ... print(key, value) ... c 2 a 0 b 1
แบบเดียวกับดิกชันนารี อิลิเมนต์ต่างๆ จะไม่ได้เรียงลำดับออกมา
มองกลับไปอีกมุมหนึ่ง เราสามารถใช้ลิสต์ของทูเพิล เพื่อกำหนดค่าเริ่มต้นให้กับดิกชันนารีใหม่ได้:
>>> t = [('a', 0), ('c', 2), ('b', 1)] >>> d = dict(t) >>> d {'a': 0, 'c': 2, 'b': 1}
ใช้ dict
ร่วมกับ zip
ช่วยให้ เรามีวิธีสร้างดิกชันนารีที่สะดวกมาก:
>>> d = dict(zip('abc', range(3))) >>> d {'a': 0, 'c': 2, 'b': 1}
เมธอด update
ของดิกชันนารี ก็สามารถรับลิสต์ของทูเพิล และเพิ่มคู่กุญแจค่าใหม่ เข้าไปในดิกชันนารีได้
เราอาจจะเห็น ทูเพิลถูกใช้เป็นกุญแจในดิกชันนารีอยู่บ่อยๆ (เพราะว่าเราใช้ลิสต์เป็นกุญแจไม่ได้) ตัวอย่างเช่น สมุดโทรศัพท์จะใช้ ชื่อและนามสกุล ไปบอกเบอร์โทรศัพท์ สมมติว่า เรากำหนด first
, last
และ number
เราอาจจะเขียน:
directory[first, last] = number
นิพจน์ในวงเล็บสี่เหลี่ยม คือ ทูเพิล เราสามารถใช้การกำหนดค่าทูเพิล เพื่อท่องสำรวจดิกชันนารีนี้ได้
for first, last in directory: print(first, last, directory[first, last])
ลูปนี้ท่องสำรวจกุญแจใน directory
โดยกุญแจเป็นทูเพิล มันกำหนดค่าของอิลิเมนต์ในแต่ละทูเพิลให้กับ first
กับ last
และพิมพ์ชื่อ นามสกุล และเบอร์โทรศัพท์ออกมา
มีสองวิธี ที่จะแสดงทูเพิลในแผนภาพสถานะ วิธีที่ละเอียด จะแสดงดัชนีและอิลิเมนต์ แบบเดียวกับที่แสดงลิสต์ ตัวอย่างเช่น ทูเพิล ('Cleese', 'John')
ก็จะแสดงแบบในรูปที่ 12.1
แต่ในแผนภาพที่ใหญ่ขึ้น เราไม่ต้องลงรายละเอียดขนาดนั้น ตัวอย่างเช่น แผนภาพของสมุดโทรศัพท์ อาจจะเป็นดังแสดงในรูปที่ 12.2 ทูเพิลในแผนภาพ แสดงง่ายๆ ด้วยไวยกรณ์ของไพธอน
เราได้ดูกันเรื่อง ลิสต์ของทูเพิล แต่เกือบทั้งหมดของตัวอย่างในบทนี้ สามารถใช้กับ ลิสต์ของลิสต์ ทูเพิลของทูเพิล และทูเพิลของลิสต์ได้ เพื่อไม่ต้องสาธยาย ทุกๆ คู่ พวกนี้ ว่าไปแล้วบางครั้ง มันก็ง่ายกว่า ที่จะอ้างถึงคู่เหล่านี้เป็น ลำดับข้อมูลของลำดับข้อมูล
ในหลายๆ บริบท ลำดับข้อมูลหลายๆ ชนิด (สายอักขระ ลิสต์ และทูเพิล) สามารถที่จะเลือกใช้อันไหนก็ได้ แล้วเราจะเลือกอย่างไร ว่าชนิดไหนดีกว่าชนิดอื่นๆ
เริ่มจากเรื่องชัดๆ ก่อน สายอักขระจะค่อนข้างจำกัดกว่าลำดับข้อมูลชนิดอื่นๆ เพราะว่า อิลิเมนต์ของมัน ต้องเป็นอักขระเท่านั้น นอกจากนั้น สายอักขระ ยังเป็นลำดับข้อมูลชนิดที่ไม่สามารถเปลี่ยนแปลงได้ ถ้าเราต้องการลำดับข้อมูลที่สามารถเปลี่ยนอักขระในลำดับได้ (ซึ่งต่างจาก การสร้างสายอักขระใหม่) เราอาจจะเลือกใช้ลิสต์ของอักขระแทน
เราจะเห็น การใช้ลิสต์บ่อยกว่าการใช้ทูเพิล ส่วนใหญ่เพราะว่า ลิสต์เป็นลำดับข้อมูลชนิดสามารถเปลี่ยนแปลงได้ แต่ก็จะมีบางกรณี ที่อาจจะเหมาะกับทูเพิลมากกว่า
return
มันสะดวกที่จะสร้างทูเพิล มากกว่าสร้างลิสต์ (ไวยากรณ์ของทูเพิลสะดวกกว่า เช่น return a,b
เปรียบเทียบกับ return [a,b]
)
เพราะว่า ทูเพิลเป็นลำดับข้อมูลชนิดที่ไม่สามารถเปลี่ยนแปลงได้ ทูเพิลจึงไม่มีพวกเมธอด เช่น sort
และ reverse
ซึ่งไปแก้ไขค่าของลิสต์ แต่ไพธอนก็ยังมีฟังก์ชันสำเร็จรูป sorted
ที่รับลำดับข้อมูล แล้วรีเทิร์นลิสต์ของอิลิเมนต์นั้นๆ ที่เรียงลำดับแล้วออกมาให้ และฟังก์ชันสำเร็จรูป reversed
ที่รับลำดับข้อมูล แล้วรีเทิร์นตัววนซ้ำที่ใช้ท่องลิสต์ในลำดับถอยหลังออกมาให้
ลิสต์ ดิกชันนารี และทูเพิล เป็นตัวอย่างของ โครงสร้างข้อมูล (data structures) ในบทนี้ เราได้เริ่มเห็นโครงสร้างข้อมูลประกอบ (compound data structure) เช่น ลิสต์ของทูเพิล หรือ ดิกชันนารีที่ใช้ทูเพิลเป็นกุญแจ และลิสต์เป็นค่า โครงสร้างข้อมูลประกอบ มีประโยชน์มาก แต่ก็เป็นความเสี่ยงให้เกิดข้อผิดพลาด แบบที่อาจจะเรียกว่า ข้อผิดพลาดจากสัดส่วน (shape error) นั่นคือ ข้อผิดพลาด ที่เกิดเมื่อใช้โครงสร้างข้อมูลผิดชนิด ผิดขนาด หรือผิดโครงสร้าง ตัวอย่าง ถ้าโปรแกรมหรือฟังก์ชันรอรับลิสต์ที่มีเลขจำนวนเต็มหนึ่งตัว แต่เราให้เลขจำนวนเต็มหนึ่งตัว (เลขจำนวนเต็มแบบดั่งเดิม ไม่ใช่อยู่ในลิสต์) มันจะไม่ทำงาน
เพื่อจะช่วยดีบักข้อผิดพลาดจากสัดส่วนพวกนี้ หนังสือเล่มได้เตรียมโมดูลพิเศษไว้ให้ ชื่อว่า structshape
ที่มีฟังก์ชัน structshape
ที่รับโครงสร้างข้อมูลใดๆ เป็นอาร์กิวเมนต์ แล้วรีเทิร์นสายอักขระที่สรุปสัดส่วนของข้อมูลออกมา โมดูลนี้สามารถดาวน์โหลดได้จาก http://thinkpython2.com/code/structshape.py
ตัวอย่างผลลัพธ์สำหรับลิสต์ง่ายๆ
>>> from structshape import structshape >>> t = [1, 2, 3] >>> structshape(t) 'list of 3 int'
โปรแกรมที่หรูหราซับซ้อนขึ้น อาจจะสามารถรายงาน “list of 3 ints” ออกมาได้ แต่ว่ามันง่ายกว่า ที่จะไม่ต้องไปยุ่งกับรูปพหูพจน์ของภาษาอังกฤษ
อันนี้เป็นตัวอย่าง เมื่อใช้กับลิสต์ของลิสต์
>>> t2 = [[1,2], [3,4], [5,6]] >>> structshape(t2) 'list of 3 list of 2 int'
ถ้าอิลิเมนต์ของลิสต์ไม่ใช่ชนิดเดียวกัน structshape
จับมันรวมกลุ่มกัน ตามลำดับชนิด ดังเช่น
>>> t3 = [1, 2, 3, 4.0, '5', '6', [7], [8], 9] >>> structshape(t3) 'list of (3 int, float, 2 str, 2 list of int, int)'
อันนี้ตัวอย่าง ลิสต์ของทูเพิล
>>> s = 'abc' >>> lt = list(zip(t, s)) >>> structshape(lt) 'list of 3 tuple of (int, str)'
และอันนี้ตัวอย่าง ดิกชันนารีที่มี 3 รายการที่แปลงเลขจำนวนเต็มไปเป็นสายอักขระ
>>> d = dict(lt) >>> structshape(d) 'dict of 3 int->str'
ถ้าเรามีปัญหาการตรวจสอบโครงสร้างข้อมูล structshape
อาจจะช่วยได้
zip
ซึ่งเป็นออปเจ็คต์ที่สามารถใช้ท่องสำรวจลำดับข้อมูลที่ได้ จากการประกอบสองลำดับข้อมูลเข้าด้วยกัน
แบบฝึกหัด 1
เขียนฟังก์ชันชื่อ most_frequent
ที่รับสายอักขระ และพิมพ์ตัวอักษรออกมา โดยเรียงลำดับตามความถี่จากน้อยไปมาก ลองหาข้อความตัวอย่างจากหลายๆ ภาษา และดูว่าความถี่ของแต่ละอักษรแตกต่างอย่างไรบ้างสำหรับแต่ละภาษา ลองเปรียบเทียบผลลัพธ์ที่ได้กับตารางใน http://en.wikipedia.org/wiki/Letter_frequencies
เฉลย: http://thinkpython2.com/code/most_frequent.py
แบบฝึกหัด 2
คำสลับอักษรอีก!
ข้างล่างเป็นตัวอย่างของเอาต์พุต
['deltas', 'desalt', 'lasted', 'salted', 'slated', 'staled'] ['retainers', 'ternaries'] ['generating', 'greatening'] ['resmelts', 'smelters', 'termless']
คำใบ้: เราอาจจะต้องสร้างดิกชันนารี ที่แปลงจากกลุ่มหมู่ของตัวอักษร ไปเป็นลิสต์ของคำ ที่สามารถสะกดด้วยอักษรเหล่านั้น คำถามคือ เราจะใช้กลุ่มหมู่ของตัวอักษรไปเป็นกุญแจของดิกชันนารีได้อย่างไร
คำใบ้: มีเจ็ดคำ.
เฉลย: http://thinkpython2.com/code/anagram_sets.py.
แบบฝึกหัด 3
คำสองคำจะเรียกว่าเป็น “คู่สลับเสียง” (metathesis pair) ถ้าเราสามารถแปลงคำหนึ่งเป็นอีกคำได้โดยการสลับสองตัวอักษร เช่น “converse” และ “conserve” เขียนโปรแกรมที่หาคู่สลับเสียงทั้งหมดออกมาจากพจนานุกรม คำใบ้: อย่าทดสอบทุกๆ คู่ของคำ และอย่าทดสอบทุกๆ การสลับอักษร
เฉลย: http://thinkpython2.com/code/metathesis.py.
ขอบคุณ: แบบฝึกหัดนี้ได้รับแรงบันดาลใจจากตัวอย่างใน http://puzzlers.org
แบบฝึกหัด 4
อีกปริศนาจากจอมปริศนาคาร์ทอร์ค (http://www.cartalk.com/content/puzzlers):
คำไหนในภาษาอังกฤษที่ยาวที่สุด ที่ถ้าดึงตัวอักษรออกทีละตัว แล้วส่วนที่เหลือยังเป็นคำในภาษาอังกฤษอยู่
ตัวอักษรที่เอาออกอาจจะเป็นตัวที่ต้น หรือปลาย หรือกลางคำก็ได้ แต่ต้องไม่มีการเรียงตัวอักษรที่เหลือใหม่ ทุกๆ ครั้งที่เอาตัวอักษรออก ส่วนที่เหลือต้องเป็นคำภาษาอังกฤษ ทำไปเรื่อยๆ สุดท้ายเราก็จะเหลือแต่ตัวอักษรตัวเดียว และตัวอักษรตัวสุดท้ายที่เหลืออยู่ก็ต้องเป็นคำในภาษาอังกฤษด้วย (มีอยู่ในพจนานุกกรม) ผมอยากจะรู้ว่า คำแบบนี้ที่ยาวที่สุดคือคำว่าอะไร และมันยาวเท่าไร?
ผมยกตัวอย่างเล็กๆ ให้อันหนึ่ง คำว่า sprite ถ้าเราเริ่มด้วย sprite เราเอาตัวอักษรหนึ่งออก เอาตัว r ออกจากกลางคำ เราจะเหลือ spite จากนั้นเอา e ออกจากท้าย เราเหลือ spit เอา s ออก เราเหลือ pit แล้ว it แล้ว I
เขียนโปรแกรมที่หาคำทั้งหมดที่สามารถลดรูปได้แบบนี้ แล้วหาคำที่ยาวที่สุด
แบบฝึกหัดนี้ค่อนข้างจะยากกว่าอันอื่นๆ คำแนะนำคือ
words.txt
ไม่มีคำอักษรเดี่ยว ดังนั้นเราอาจต้องเพิ่ม “I” และ “a” และสายอักขระว่างๆ เข้าไปเฉลย: http://thinkpython2.com/code/reducible.py.
https://greenteapress.com/thinkpython2/html/thinkpython2013.html