กระรอกดินต้นฉบับแบ่งเมล็ดบนสายพานเป็นถาดละสามเม็ด โดยขอบถาดป้องกันไม่ให้ไหลปนกัน

Go slices.Chunk: แบ่ง batch โดยไม่เปิดช่องให้ append ทับข้อมูลข้างเคียง

พี่โชว์Engineering

งานส่ง API, เขียนฐานข้อมูล หรือประมวลผลไฟล์มักต้องแบ่ง input เป็นก้อนขนาดคงที่ โค้ดแบบ for start := 0; start < len(values); start += size ทำได้ แต่ต้องคำนวณปลายช่วงเองและคุมกรณีก้อนสุดท้ายทุกครั้ง slices.Chunk ทำเรื่องนี้เป็น iterator มาตรฐาน: รับ slice กับขนาดสูงสุด แล้ว yield subslice ที่ต่อเนื่องกันครับ

จุดที่น่าสนใจกว่าความสั้นคือทุกก้อนถูก clip capacity ให้เท่ากับ length สมมติ batch แรกยาว 3 การ append สมาชิกที่สี่จะต้องจัด backing array ใหม่ จึงไม่เขียนทับสมาชิกต้นก้อนถัดไปโดยบังเอิญ อย่างไรก็ดี สมาชิกเดิมใน batch ยังแชร์ storage กับ input การแก้ batch[0] จึงสะท้อนกลับไปยัง slice ต้นทาง

package main

import (
	"fmt"
	"slices"
)

func main() {
	values := []int{10, 20, 30, 40, 50, 60, 70}
	var sizes []int
	for batch := range slices.Chunk(values, 3) {
		sizes = append(sizes, len(batch))
		if len(batch) != cap(batch) {
			panic("Chunk must clip each batch capacity")
		}
	}
	if !slices.Equal(sizes, []int{3, 3, 1}) {
		panic(fmt.Sprintf("unexpected sizes: %v", sizes))
	}

	first := slices.Collect(slices.Chunk(values, 3))[0]
	appended := append(first, 99)
	if values[3] != 40 || appended[3] != 99 {
		panic("append crossed a batch boundary")
	}
	fmt.Println(sizes)
}

ใช้ให้ตรงสัญญา

Chunk จะ panic เมื่อ n < 1 ดังนั้นค่าที่มาจาก config หรือ request ต้อง validate ก่อน ถ้า input ว่าง iterator ไม่ yield อะไร ซึ่งต่างจากบาง helper ที่อาจคืนหนึ่งก้อนว่าง อีกด้านหนึ่ง iterator ช่วยให้หยุดก่อนครบทุกก้อนได้ จึงเหมาะกับ pipeline ที่อาจเจอ error กลางทางและไม่ต้องสร้าง [][]T ทั้งก้อนล่วงหน้า

หาก worker ทำงานขนานและแก้สมาชิกใน batch ต้องพิสูจน์ว่าแต่ละ batch ไม่ซ้อนกันจริงและไม่มี goroutine อื่นแตะ input พร้อมกัน แม้ capacity ถูก clip ก็ไม่ได้เปลี่ยน subslice ให้เป็นสำเนา ถ้าต้องส่ง ownership ออกจากขอบเขตหรือเก็บไว้นานกว่าต้นทาง ให้ clone แต่ละก้อนด้วย slices.Clone ก่อน

Recap

  • Chunk ลด arithmetic ของขอบช่วงและจัดการก้อนสุดท้ายให้
  • len(batch) == cap(batch) กัน append ไหลเข้าก้อนถัดไป แต่สมาชิกยังแชร์ storage
  • validate ว่าขนาด batch มากกว่าศูนย์
  • clone เมื่อผู้รับต้องถือ ownership หรือแก้ข้อมูลอิสระครับ