การเขียน Loop ใน Python หรือใน Programming Language ไหนก็น่าจะเป็นเรื่องพื้นฐานที่เราจะต้องทำความเข้าใจมาก่อนอยู่แล้ว ซึ่งมันอยู่ในแทบจะทุกส่วนของ Code ที่เราเขียนอยู่บ้างแหละ เมื่อก่อน ตอนเราเรียน Loop ใน Python เราก็อาจจะเคยได้ลอง Loop โดยให้มันเอาข้อมูลใน List ออกมา หรืออาจจะให้มันนับเลขด้วยคำสั่ง range นี่เป็นส่วนใหญ่ที่เราจะใช้งานกันบ่อย ๆ แล้ว แต่บางทีมันจะมีเคสที่เราอาจจะใช้ทั้ง 2 อย่างไม่ได้ หรือมันอาจจะต้องมีการดัดแปลง ทำให้ Code เราเข้าใจยากอีก วันนี้เลยจะพามาทำความรู้จักอีกหนึ่ง Tools ที่ Python เตรียมมาให้เรานั่นคือ Itertools
import itertools
ก่อนเราจะเริ่มใช้ Iteratools เราจะต้อง Import มันเข้ามาก่อน ผ่านคำสั่งด้านบน และไม่ต้องมีการ Install อะไรทั้งนั้น เพราะเป็น Built-in Module จาก Python เลย ง่ายมาก ๆ
ซึ่งใน Itertools เราสามารถแบ่งประเภทของ Iterator อยู่ 2 ประเภทคือ Infinite Iterators และ Finite Iterators หรือก็คือ Iterator ที่วนได้ไม่มีวันสิ้นสุด กับ วนแบบมีจุดสิ้นสุดนั่นเอง อันนี้สามารถนำไปใช้กับเรื่องของ Iterator ได้อีกด้วยนะ (ไว้บทความหน้าจะมาเล่าเรื่องของ Iterator กันลึก ๆ) ในวันนี้เราจะเอามาเล่าทั้งหมด 6 ตัวที่เราใช้บ่อย ๆ แบ่งเป็น Finite Iterators คือ Count, Cycle และ Repeat ส่วน Infinite Iterators คือ Chain, Compress และ DropWhile
counter = 0
while True:
counter += 1
print(counter)
if counter == target:
break
เริ่มจากตัวแรกคือ Count ที่เป็นคำสั่งที่ทำตามชื่อตรง ๆ เลยคือการนับ เริ่มจากเมื่อก่อน ถ้าเราจะนับเลข เราก็อาจจะใช้ ตัวแปรในการนับ พอถึงจุดนึง เราก็เขียน Condition ให้มันหยุด ก็จะเป็นแบบ Code ด้านบนเลย
for counter in range(0,target):
print(counter)
หรืออีกวิธี เราอาจจะใช้ Iterator อย่าง Range ในการช่วยนับ แต่ ๆ เราก็ต้องมานั่งกำหนด ต้น และ ปลายของการนับ ถ้าเกิด เราต้องมีการเปลี่ยนแปลงค่า Target ระหว่างการนับ Range ก็จะไม่ตอบโจทย์ทันที ทำให้ Count เข้ามาช่วยเราในเรื่องนี้ได้
import itertools
for counter in itertools.count(start=0, step=1):
print(counter)
ด้านบนเป็นตัวอย่างของการใช้คำสั่ง count เข้ามาช่วย เราจะเห็นว่า เราให้มันเริ่มนับที่ 0 และเพิ่มขึ้นทีละ 1 ไปเรื่อย ๆ ใน Loop เราก็ให้มันเอา Counter ออกมาบนหน้าจอ แต่สังเกตุอะไรมั้ยฮ่ะว่า โปรแกรมนี้มันขาดอะไร ใช่แล้ว มันขาด Terminating Condition ใน Loop ทำให้เมื่อเรารันโปรแกรมนี้ขึ้นมา มันก็จะนับ Counter ไปจนกว่า Integer จะเต็มเลย ซึ่งนั่นไม่ใช่สิ่งที่เราอยากจะทำแน่ ๆ
import itertools
for counter in itertools.count(start=0, step=1):
if counter > target :
break
print(counter)
ดังนั้น เพื่อป้องกันปัญหาดังกล่าว เราก็ต้องเพิ่ม Terminating Condition ลงไปนั่นเอง เพื่อให้ Loop มันมีที่หยุด ซึ่งจริง ๆ คิดดี ๆ แล้ว มันก็จะเหมือนกับ While Loop ที่เขียนให้ดูข้างบนแรก ๆ เลย แต่จะเห็นว่า Code มันดูเรียบร้อยกว่าเยอะมาก
ex_list = ['a', 'b', 'c']
for i in range(loop_round):
for j in range(len(ex_list)):
print(ex_list[j])
Cycle เป็นคำสั่งที่มันจะ Loop ไปใน Iterator ต่าง ๆ ไปเรื่อย ๆ ตัวอย่างจากด้านบน เราจะเห็นว่า เรามี List ตัวนึงที่เก็บข้อมูลอยู่ทั้งหมด 3 ตัวด้วยกัน สิ่งที่เราอยากได้คือ เราอยากให้มันวนไปตามข้อมูลใน List ไปเรื่อย ๆ ถามว่า เราจะทำยังไงดี ถ้าคิดเร็ว ๆ เราก็อาจจะใช้ For-Loop 2 ชั้นด้วยกัน โดยที่ชั้นนึงจัดการกับรอบ ว่าเราจะวน List กี่รอบ (i) และใช้วนภายใน List ก็คือจะต้องวนตามจำนวนสมาชิกใน List เราจะเห็นได้ว่า หน้าตาของมันไม่ได้ดีเลย และเราเขียน Loop แบบนี้ ถ้าใครที่เรียนเรื่อง Big-O Notation มาอาจจะไม่ถูกใจสิ่งนี้แน่ ๆ เพราะมันให้ O(n^2) ซึ่ง ไม่น่ารักเลย เราทำยังไงถึงจะดีกว่านี้ได้
ex_list = ['a', 'b', 'c']
for i in range(loop_round * len(ex_list)) :
print(data[i%len(ex_list)])
เราลองมาปรับปรุงเพื่อทำให้ Code มันซับซ้อนน้อยลงกันดีกว่า นั่นก็คือ Code ด้านบนนี้เอง เราบอกว่า เรารู้อยู่แล้วว่า เราจะต้อง Loop เป็นรอบ ๆ ไปตาม List ของเรา และ รู้ว่า List ของเราขนาดเท่าไหร่ จากคำสั่ง len งั้นเราลองใช้ Modulo มั้ย เราก็แค่ Modulo ด้วยขนาดของ List เช่น List มีสมาชิกทั้งหมด 3 ตัว ถ้าเรา Modulo ออกมา เราก็จะได้แค่ 0,1,2 ซึ่งก็จะเป็นเลข Index พอดี และถ้าเราลอง Loop ออกมาดู เลขมันจะได้ออกมาเป็น 0,1,2 วนไปเรื่อย ๆ ไม่ว่าเราจะเริ่มจำนวนรอบไปเท่าไหร่ก็ตาม แต่เอาจริง ๆ เถอะ มันก็แอบดู งง มั้ยนะ ถ้าเป็นเรามาอ่าน Code ชุดนี้ที่คนอื่นเขียนไว้ ก็ต้องใช้เวลานิดนึงเลย ในการนั่งอ่าน และทำความเข้าใจ ทำให้มันไม่เหมาะกับการเอามาเขียนจริง ๆ ใช้ในงานจริง ๆ เท่าไหร่ เมื่อเรามีสิ่งที่ดีกว่า
import itertools
ex_list = ['a', 'b', 'c']
counter = 0
for ex_char in itertools.cycle(ex_list):
if counter == loop_round * ex_list :
break
print(ex_char)
counter += 1
เมื่อเราลองเอา Cycle มาใส่ เราจะเห็นได้ชัดเลยว่า Code มันอ่านได้ง่าย เข้าใจง่ายกว่า เพราะมันตัดพวก Logic แปลก ๆ ออกไปได้หมดเลย ไม่มีพวก Modulo หรือ Loop ซ้อน Loop อะไรให้ งง เขียนตรง ๆ ไปเลย ดูดีกว่าเห็น ๆ เลยใช่มั้ยล่าาา
word = 'stackpython'
for i in range(loop_count):
print(word)
ใน Cycle มันเป็นการ Loop ใน Iterator ต่าง ๆ แล้วถ้าเราอยากจะ Loop ของเดิมกลับมาเลย ละ เป็นเคสที่แปลก ๆ ใช่มั้ย แต่เราก็เจอมาหลายรอบแล้วเหมือนกัน อาจจะเป็นการ Update อะไรบางอย่าง หรือเวลาเราทำโปรแกรมที่มันใช้วิธีที่เป็น Numerical Method ที่เราจะต้องวนชุดข้อมูลเดิม ๆ ซ้ำไปเรื่อย ๆ นี่แหละ เจอเต็ม ๆ ตาเลย ซึ่งเราก็จะใช้คล้าย ๆ กับ Code ด้านบนเลยคือ เรามีข้อมูลบางอย่างอยู่ และ เราใช้ For-Loop ที่นับครั้งด้วยการใช้คำส่ัง range แล้วทำอะไรบางอย่างกับมัน
แต่ปัญหาคือ เราก็จะมี i ที่เราไม่ได้ใช้อยู่ ถ้าเราใช้ Linter มันก็จะด่าเราว่า ทำไมเราสร้าง i แล้วเราไม่ใช้ละ หรือ เอาจริง ๆ เวลาเราเขียนไป แล้วเรากลับมาอ่านเอง หรือคนในทีมกลับมาอ่านก็น่าจะ งง ๆ ว่า แล้ว i ที่เราเขียนลงไป มันเอาไปทำอะไร หรือ Code ข้างล่างที่ใช้ i หายไปหรือยังไง ซึ่งเป็นเรื่องที่บอกเลยว่า ไม่ตลกแน่ ๆ
import itertools
word = 'stackpython'
for word in itertools.repeat(word, times=loop_count) :
print(word)
ใน Repeat เราก็แค่ใส่ของที่เราต้องการให้มันวนซำ้เข้าไป และเราสามารถกำหนด times หรือจำนวนครั้งที่จะให้มันวนก็ได้ หรือ เราจะไม่กำหนดก็ได้ แต่ถ้าเราไม่กำหนด อย่าลืมสร้าง Terminate Condition ลงไปด้วย ไม่งั้นเราจะเจอ Loop วนไม่รู้จบเลยนะ อันนี้แย่กว่า Count ด้วย เพราะ มันวนค่าเดิม ไม่มี Integer เต็มอะไรทั้งนั้น
list_a = [1,2,3]
list_b = [4,5,6]
list_c = [7,8,9]
for item in list_a + list_b + list_c :
print(item)
มาถึง Finite Iterators ตัวแรกคือ Chain มันทำหน้าที่เหมือนโซ่ที่คล้องหลาย ๆ Iterator เข้าหากัน จากตัวอย่างด้านบน เป็นตัวอย่างที่ถ้าเราไม่ใช้ Chain เราจะทำยังไง มันเริ่มจาก เรามี List อยู่ทั้งหมด 3 ตัว และ เราต้องการที่จะ Loop มันทั้งหมดเลย วิธีที่คิดได้เร็ว ๆ ก็คือ เอา List ทั้งหมดมาบวกกัน ซึ่งการบวก List ใน Python ก็คือ Concatinate หรือ การเอา List มาเชื่อมกันนั่นเอง มันก็จะได้ออกมาเป็น List อันนึงแล้วก็ Loop ได้ตามปกติเลย
ปัญหามีอยู่ว่า เอาจริง ๆ เราว่า การบวก List มันดูตลกไปนิดนึง มันต้องอาศัยความเข้าใจในเรื่องของเครื่องหมายกับ Data Type ประเภทอื่น ๆ เรามองว่า มันก็เป็น Shorthands ที่ดี แต่มันเข้าใจยาก
for item in list_a + list_b + list_c :
เรื่องของ Execution Order ลองดูที่ For-Loop เราจะเห็นว่า มันมี Expression ซ้อนอยู่ข้างในคือตรงที่เราบวกสมาชิกใน List เข้าหากัน แปลว่ามันต้องเข้าไปจองพื้นที่ใน Memory, บวกสมาชิกใน List เข้าไป และ เอาผลลัพธ์ไปใส่ใน Memory แล้วค่อย Loop ไปเรื่อย ๆ ซึ่งถ้าเป็น ข้อมูลขนาดเล็กดังตัวอย่างด้านบนจะไม่มีปัญหาเลย ยังไง CPU เราก็เอาอยู่ และ Memory เราก็มีพออยู่แล้ว แต่ถ้าเราบอกว่า เราทำงานกับ 10 List และในแต่ละ List มีสมาชิกอยู่ 2000 M ก็คือ List ที่มันสร้างขึ้นมาใหม่ มีขนาด 20,000 M เลยนะ ถ้าเราบอกว่ามันเป็น Integer ขนาด 4 Byte ลองคูณดูว่า เราต้องใช้ Memory อีกเท่าไหร่ ฮ่าไม่ออกแน่ ๆ และมันจะกำหมัดมากกว่า ถ้าเราบอกว่า เราใช้มันแค่ใน Loop เดียวเท่านั้นแหละ ยิ้มแห้งแน่
หรือจริง ๆ แล้วเราก็อาจจะเขียน Loop แล้วมี Counter นี่แหละ ประกอบร่างกับ Modulo ในการกำหนดช่วง และ ใช้ Conditional ในการแบ่งช่วงเลยว่า ถ้า Mod ออกมาแล้วมันตกค่านี้ มันจะต้อง Route ไป List ไหนก็เอาเลย เป็น Challenge ลองเขียนดูขำ ๆ ได้
import itertools
looper = itertools.chain(list_a, list_b, list_c)
for item in looper:
print(item)
เมื่อเราเอา Chain มาใช้ เราจะเห็นได้เลยว่า Code ของเราสั้น กระชับ ขึ้นเยอะมาก ลืมเรื่อง Logic ที่เราเล่ามาได้เลย เขา Implement มาให้เราหมดแล้ว ไม่ต้องคิดอะไรอีก ช่วยทำให้เราประหยัดเวลาไปได้เยอะมาก
name_list = ['Thomas', 'John', 'Sam']
attendance = [False, True, True]
attened_name = [name_list[i] if attendance[i] else '' for i in range(len(name_list))]
attened_name = [current_name for current_name in attened_name if current_name != '']
print('Name of attendance are ' + ', '.join(attened_name))
Compress ตามชื่อเลยคือ เข็มทิศ อาจจะ งง แน่ ๆ ว่า เข็มทิศ มันเกี่ยวอะไรกับการ Loop เราลองมาดูตัวอย่างด้านบนกันก่อน เรามี List อยู่ 2 ตัวด้วยกันคือ Name List สำหรับเก็บชื่อของคนที่จะต้องเข้าประชุม และ Attendance เป็น List สำหรับเก็บว่าคน ๆ นั้นเข้าประชุมจริงมั้ย สิ่งที่เราต้องการคือ รายชื่อของคนที่เข้าประชุมทั้งหมด วิธีแบบคิดเร็ว ๆ แว่บแรกเลย เราบอกว่า เราก็สร้าง List ขึ้นมาตัวนึง เราขอใช้ List Comprehension ละกัน เพื่อความเท่ (ได้เหรอ ?) โดยที่ Loop เป็นตัวเลขที่เป็นขนาดของ Name List ซึ่งเราสมมุติเลยว่า Name List และ Attendance เป็นข้อมูลที่สมบูรณ์ กล่าวคือ มีขนาดที่เท่ากัน และ เราก็เช็คเลยว่า ถ้าสมาชิกใน Attendance ตัวที่กำลัง Loop อยู่เป็น True ก็ให้มันเอา Name List ตัวที่ i นั่นแหละ ไปใส่ใน List ใหม่ และ ถ้าเป็น False ก็ให้มันเอา String ว่างไปใส่
ทำให้ เมื่อเราลองเช็ค List ที่เราสร้างขึ้นมาใหม่ เราจะพบว่ามันมีสมาชิกตัวนึงเป็น String ว่าง หรือก็คือ Thomas นั่นเอง ที่ไม่ได้เข้าประชุม แหม่ แต่เราก็ไปบังคับ Thomas ให้เข้าไม่ได้ใช่มั้ย ฮ่า ๆ ทำให้เราต้องมา Filter String ว่างที่เรา Generate ขึ้นมาออกไป เราก็ใช้ List Comprehension ช่วยเหมือนเดิมเลย เราก็ Loop ไปใน Attened Name ที่เราสร้างออกมา และเช็คว่า อันไหนที่เป็น String ว่าง เราก็ไม่เอามา ทำให้เราได้ List ของคนที่มาแบบ Clean แล้ว สุดท้าย เราก็ Print ชื่อออกมา อย่างเก๋ ๆ ด้วยการใช้คำสั่ง Join อันนี้ลองไปอ่านเพิ่มดูได้
import itertools
looper = itertools.compress(name_list, attendance)
attened_name = [name for name in looper]
print('Name of attendance are ' + ', '.join(attened_name))
compressed_words = [0, 0, 0, 0, 1, 1, 1, 0, 1]
is_ended = False
drop_condition = 0
for word in compressed_word:
if is_ended is False and word != drop_condition:
is_ended = True
if is_ended :
print(compressed_word[word_count])
import itertools
for word in itertools.dropwhile(lambda x : x == 0, compressed_words))) :
print(word)
กิจกรรมที่กำลังจะมาถึง
ไม่พลาดกิจกรรมเด็ด ๆ ที่น่าสนใจ
Event นี้จะเริ่มขึ้นใน April 25, 2023
รายละเอียดเพิ่มเติม/สมัครเข้าร่วมคอร์สเรียนไพธอนออนไลน์ที่เราได้รวบรวมและได้ย่อยจากประสบการณ์จริงและเพื่อย่นระยะเวลาในการเรียนรู้ ลองผิด ลองถูกด้วยตัวเองมาให้แล้ว เพราะเวลามีค่าเป็นอย่างยิ่ง พร้อมด้วยการซัพพอร์ตอย่างดี