ข้ามไปยังเนื้อหา

Replace Loop with Pipeline

loop ที่เดินบน collection ข้ามบางสมาชิก แปลงบางตัว แล้วสะสมผลลัพธ์ คือการทำงานหลายอย่างพันกันอยู่ collection pipeline คลายปมนั้นออก แต่ละ operation — filter, map, reduce — กลายเป็นขั้นตอนแยกกันที่มีชื่อ และข้อมูลไหลผ่านตามลำดับ ผู้อ่านตามข้อมูลไป แทนที่จะต้องคอยตาม accumulator ที่เปลี่ยนค่าได้กับ index ส่วน behavior เหมือนเดิมทุกอย่าง เปลี่ยนแค่รูปทรงจาก “วน loop ยังไง” เป็น “แต่ละขั้นทำอะไร”

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

งานคือเก็บชื่อลูกค้า premium ใน region หนึ่ง แบบ imperative เป็น loop เดียวที่รวม guard การอ่าน field และการ append ไว้ด้วยกัน ส่วนแบบ pipeline คือ filter แล้ว filter แล้ว map

// Before
function premiumNames(customers: Customer[], region: string): string[] {
const names: string[] = [];
for (const c of customers) {
if (c.region !== region) continue;
if (!c.isPremium) continue;
names.push(c.name.toUpperCase());
}
return names;
}
// After
function premiumNames(customers: Customer[], region: string): string[] {
return customers
.filter((c) => c.region === region)
.filter((c) => c.isPremium)
.map((c) => c.name.toUpperCase());
}
  1. หา collection ที่ loop เดินผ่าน และระบุว่าผลลัพธ์ที่สร้างออกมาคืออะไร
  2. แปล logic แต่ละส่วนของ loop เป็นขั้นตอนหนึ่งขั้น guard แบบ continue กลายเป็น filter การแปลงค่าต่อสมาชิกกลายเป็น map การสะสมเป็นค่าเดียวกลายเป็น reduce (หรือ sum, join และพวกพ้อง)
  3. ต่อ pipeline ทีละขั้น จะปล่อย loop เดิมไว้ข้าง ๆ ไว้เทียบก็ได้
  4. แทนที่ loop ด้วย pipeline ที่ต่อเสร็จแล้ว แล้ว return ผลลัพธ์ออกไป
  5. รัน test ผลลัพธ์ต้องตรงกับ loop เป๊ะ รวมถึงเคส input ว่างด้วย
  6. ในภาษาที่ไม่มี pipeline ในตัวอย่าง Go อย่าฝืนสร้างขึ้นมา ใช้ loop เดียวต่อไปแต่ยกแต่ละเงื่อนไขขึ้นเป็น boolean ที่มีชื่อชัด ๆ ขั้นตอนจะได้ยังอ่านเป็นแนวคิดที่แยกจากกัน

หยิบ pipeline มาใช้เมื่อ loop ปน filter, transform และ accumulate เข้าด้วยกัน การซอยเป็นขั้น ๆ ทำให้แต่ละเจตนาชัด และอ่านเส้นทางของข้อมูลไล่จากบนลงล่างได้ ยิ่งได้ผลเมื่อ operation เป็น pure และไม่ขึ้นกับลำดับ

ข้อแลกเปลี่ยนคือ loop ที่มี break ออกกลางทาง มี index ที่มีความหมาย หรือมี side effect ใน body พอแปลงเป็น pipeline แล้วมักออกมาอึดอัดหรือชวนเข้าใจผิด และ chain ที่ยาวเกินไปก็กลายเป็นปริศนาชุดใหม่ได้เหมือนกัน pipeline บางตัวยังจอง collection กลางเพิ่มด้วย ใช้ตรงจุดที่ทำให้ชัดขึ้นเท่านั้น และอย่าลืมข้อสังเกตเรื่อง Go ข้างบน — loop ที่ซอยเป็นขั้นตอนมีชื่อคือของเทียบเท่าที่เข้ากับสไตล์ Go มากกว่า

ใช้ Replace Loop with Pipeline เมื่อหลีกเลี่ยงเมื่อ
loop ทำหลายขั้น: filter + transform + reduceloop มี side effect (I/O, mutation) ใน body
ต้องการ chain operation หลายตัวเป็น readable flowทีมไม่คุ้นกับ functional style — อ่านยากกว่า loop
logic ใน loop อ่านยากเพราะมีหลาย conditionperformance สำคัญมาก — pipeline สร้าง intermediate collection

⚠️ ไม่ควร Replace Loop with Pipeline เมื่อ:

  • loop ต้องใช้ index ของ element (for i, v in enumerate(...))
  • loop มีการ break กลางคัน — pipeline ไม่รองรับ early exit แบบง่าย
  • ต้องการความเร็วสูงสุด — pipeline สร้าง allocation เพิ่ม
Replace Loop with Pipeline ทำให้อะไรชัดเจนขึ้น?
guard แบบ "continue" ใน loop มักกลายเป็นขั้นตอน pipeline แบบใด?
Go ไม่มี pipeline ในตัว บทเรียนนี้แนะนำให้ทำอย่างไร?
loop แบบไหนที่ไม่เหมาะจะเปลี่ยนเป็น pipeline?