เรียน Python แล้วต้องลอง itertools: จาก Loop ธรรมดาสู่การคิดแบบ Data-Driven
ถ้าถามว่ามีโมดูลไหนใน Python ที่ถูกประเมินค่าต่ำกว่าความสามารถจริงมากที่สุด itertools น่าจะติดอันดับต้น ๆ
หลายคนรู้จักมันแค่
from itertools import combinations
เอาไว้สุ่มจับคู่ หรือสร้างชุดข้อมูล
แต่เมื่อเริ่มทำระบบจริง ไม่ว่าจะเป็นเกม, AI, Rule Engine, State Machine หรือ Data Processing จะพบว่า itertools ไม่ได้เป็นแค่เครื่องมือช่วยวนลูป
มันคือชุดเครื่องมือที่ช่วยให้เรา "เปลี่ยนวิธีคิด"
จาก
if ...
elif ...
elif ...
ไปสู่
rules[(state, event)]
จาก
for ...
for ...
for ...
ไปสู่
product(...)
จาก
current_player = player1
...
current_player = player2
ไปสู่
next(turns)
product(): เครื่องจักรสร้างความเป็นไปได้
หนึ่งในฟังก์ชันที่ผมชอบที่สุดคือ
from itertools import product
สมมติเราทำเกมการ์ด
มี
elements = ["Fire", "Water", "Wind"]
powers = [10, 20, 30]
เราสามารถสร้างการ์ดทุกใบที่เป็นไปได้ด้วย
cards = list(product(elements, powers))
ผลลัพธ์
[
('Fire', 10),
('Fire', 20),
('Fire', 30),
('Water', 10),
...
]
โดยไม่ต้องเขียน Loop ซ้อนเอง
ทันทีที่เห็นสิ่งนี้ เราจะเริ่มมองข้อมูลเป็น "ชุดคุณสมบัติ" มากกว่า "รายการที่ต้องสร้างทีละชิ้น"
ซึ่งเป็นแนวคิดเดียวกับการสร้างไอเทม RPG, Monster, Skill หรือ Card Game ในโลกจริง
combinations(): สร้าง Deck โดยไม่ต้องคิดเอง
เมื่อมีการ์ดทั้งหมดแล้ว
สมมติเลือก 5 ใบเป็น Deck
from itertools import combinations
all_decks = combinations(cards, 5)
สิ่งที่ได้คือทุก Deck ที่เป็นไปได้
โดยไม่ต้องเขียน Logic สลับตำแหน่งหรือวนลูปหลายชั้น
แนวคิดนี้ใช้ได้กับ
จัดทีม
เลือกไอเทม
สร้าง Loadout
วิเคราะห์ความเป็นไปได้ของเกม
cycle(): Turn-Based แบบสวยงาม
หลายคนเขียนระบบ Turn-Based แบบนี้
if current_player == player1:
current_player = player2
else:
current_player = player1
แต่ cycle() ทำให้เรื่องนี้กลายเป็น
from itertools import cycle
turns = cycle([
player1,
player2
])
current_player = next(turns)
เรียบง่ายมาก
ยิ่งถ้ามี 4 คน
cycle([
player1,
player2,
player3,
player4
])
ก็ทำงานได้ทันที
itertools ทำให้เห็น Rule มากกว่า Code
ระหว่างคุยเรื่องเกม เราได้ลองสร้างระบบแพ้ทางธาตุ
หลายคนอาจเขียน
if atk == "Fire" and defend == "Wind":
damage *= 2
elif atk == "Wind" and defend == "Water":
damage *= 2
แต่พอใช้ Dictionary ร่วมกับ Tuple
advantage = {
("Fire", "Wind"): 2,
("Wind", "Water"): 2,
("Water", "Fire"): 2
}
และเรียก
bonus = advantage.get(
(atk, defend),
1
)
สิ่งที่เกิดขึ้นคือ
เราไม่ได้อ่าน "โค้ด"
เราอ่าน "กฎ"
ทันที
Tuple คือ Composite Key
สิ่งที่ทำให้แนวคิดนี้ทรงพลังคือ
Tuple สามารถใช้เป็น Key ของ Dictionary ได้
rules = {
("Idle", "Attack"): "Attacking",
("Idle", "Move"): "Moving",
("Moving", "Stop"): "Idle"
}
แล้ว
state = rules.get(
(state, event)
)
แทนที่จะมี
if ...
elif ...
elif ...
เต็มไปหมด
โค้ดเหลือแค่การ Lookup
เหมือนเปิดตารางกฎของเกม
เมื่อ Rule กลายเป็น Data
จุดที่น่าสนใจมากคือ
rules = {
("Fire", "Wind"): 2,
("Water", "Fire"): 2,
}
จริง ๆ แล้วคือ
Rule Engine ขนาดย่อม
เราแยก
Logic
Rule
ออกจากกัน
Engine ทำหน้าที่แค่
rules.get(key)
ส่วนกฎทั้งหมดเก็บเป็นข้อมูล
ผลคือ
เพิ่ม Rule โดยไม่แก้ Code
โหลดจาก JSON ได้
ให้ Designer ปรับค่าได้
อ่านง่ายกว่าการไล่ Call Stack
Data-Driven Design
นี่คือเหตุผลที่หลายระบบสมัยใหม่เริ่มลดการพึ่งพา Polymorphism หนัก ๆ
แทนที่จะมี
class Fireball(Card):
...
class Heal(Card):
...
class Lightning(Card):
...
หลายร้อยคลาส
เราอาจมี
cards = {
"FIREBALL": {
"damage": 30
},
"HEAL": {
"heal": 20
}
}
แล้วให้ Engine กลางจัดการ
ทันใดนั้น
การ์ดใหม่ไม่ต้องสร้างคลาสใหม่
แค่เพิ่มข้อมูล
itertools ไม่ใช่แค่เรื่อง Loop
หลายคนคิดว่า itertools เป็นแค่เครื่องมือช่วยวนลูป
แต่ยิ่งใช้มากขึ้น จะยิ่งพบว่า
มันช่วยเปลี่ยนวิธีคิดจาก
"เขียนขั้นตอน"
ไปเป็น
"อธิบายความสัมพันธ์ของข้อมูล"
จาก
for
if
elif
ไปสู่
product()
combinations()
cycle()
และสุดท้ายกลายเป็น
rules[(condition1, condition2)]
ซึ่งอ่านแล้วเหมือนกำลังเปิดตารางกฎของเกม มากกว่ากำลังอ่าน Source Code
นั่นเป็นเหตุผลที่ itertools ยังคงเป็นหนึ่งในโมดูลมาตรฐานของ Python ที่ผมชอบที่สุด เพราะมันไม่ได้ทำให้โค้ดสั้นลงอย่างเดียว แต่ทำให้โค้ด "คิดเป็นระบบ" มากขึ้นด้วย
ความคิดเห็น
แสดงความคิดเห็น