Rust slice::chunk_by: แบ่งข้อมูลติดกันตามกติกาโดยไม่ copy
ข้อมูลจาก sensor หรือ event log มักมาเป็น “ช่วง” เช่นสถานะปกติติดต่อกันหลายรายการ แล้วเปลี่ยนเป็นเตือน ถ้าเราอยากแบ่งช่วงโดยรักษาลำดับเดิม การสร้าง Vec<Vec<T>> ใหม่ตั้งแต่ต้นเปลืองทั้ง allocation และ copy ครับ slice::chunk_by ให้ iterator ของ subslice ที่ยืมข้อมูลเดิม โดย predicate ตอบว่าคู่ที่อยู่ติดกันควรอยู่กลุ่มเดียวกันหรือไม่
WHY: เกณฑ์อยู่ระหว่างสมาชิก ไม่ได้อยู่ที่สมาชิกตัวเดียว
ความต่างจาก split คือ split หา “ตัวคั่น” แล้วตัดมันทิ้ง แต่ chunk_by เก็บสมาชิกทุกตัวและถามความสัมพันธ์ของเพื่อนบ้าน เหมือนแบ่งขบวนรถไฟเป็นตอนเมื่อสีตู้เปลี่ยน ไม่มีตู้ไหนหายไป
ตัวอย่างแบ่งจำนวนเต็มเป็นช่วงติดกันตามเครื่องหมาย ศูนย์เป็นกลุ่มของตัวเองเพื่อให้กติกาชัดเจน:
fn runs_by_sign(values: &[i32]) -> Vec<&[i32]> {
values
.chunk_by(|left, right| left.signum() == right.signum())
.collect()
}
fn main() {
let values = [-3, -1, 0, 2, 8, -4];
let runs = runs_by_sign(&values);
assert_eq!(runs, vec![&[-3, -1][..], &[0][..], &[2, 8][..], &[-4][..]]);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn keeps_every_element_in_order() {
let values = [-3, -1, 0, 2, 8, -4];
assert_eq!(
runs_by_sign(&values),
vec![&[-3, -1][..], &[0][..], &[2, 8][..], &[-4][..]]
);
}
#[test]
fn empty_input_has_no_chunks() {
assert!(runs_by_sign(&[]).is_empty());
}
}
Trade-off ที่ต้องเห็น
เวลาเป็น O(n) เพราะดูแต่ละรอยต่อครั้งเดียว ส่วน iterator และ subslice ไม่ copy ข้อมูล; Vec ในตัวอย่างมีไว้เก็บ reference เพื่อทดสอบเท่านั้น ถ้า consumer ประมวลผลทีละช่วงก็ใช้หน่วยความจำเสริม O(1) ได้ ข้อแลกเปลี่ยนคือผลลัพธ์ยืม slice ต้นฉบับ จึงเก็บนานกว่าข้อมูลต้นทางไม่ได้ และ predicate ที่มี state ซับซ้อนจะทำให้โค้ดอ่านยาก
ถ้าต้องจัดกลุ่มสมาชิกที่กระจายอยู่คนละตำแหน่ง ให้ใช้ map ตาม key แทน ถ้าต้องแบ่งทุก N ตัว ใช้ chunks หรือ chunks_exact และถ้าต้องแก้ข้อมูลในแต่ละช่วง ใช้ chunk_by_mut พร้อมระวัง borrow rule
Recap
- ใช้เมื่อ “ความต่อเนื่อง” สำคัญกว่าการ group ทั้งชุด
- นิยาม predicate ของสมาชิกติดกันให้รีวิวได้ง่าย
- ทดสอบ input ว่าง จุดเปลี่ยนติดกัน และกลุ่มเดียวทั้ง slice
- ประมวลผล iterator ตรง ๆ ถ้าไม่จำเป็นต้องเก็บทุก subslice