อิกัวนาตัวเล็กต้นฉบับเลื่อนกรอบสีอำพันครอบก้อนข้อมูลสีเขียวทีละช่วงโดยไม่ยกก้อนออกจากราง

Zig std.mem.window: ทำ Sliding Window โดยคืน Slice ไม่คัดลอกข้อมูล

พี่โชว์Engineering

moving average, n-gram และการอ่านข้อมูลเป็น batch มีรูปแบบเดียวกัน: มอง buffer ทีละหน้าต่าง แล้วเลื่อนไปข้างหน้า ถ้าเขียน index เองทุกครั้ง จุดพลาดมักอยู่ที่ท้ายข้อมูลและระยะเลื่อน std.mem.window รวมกติกานี้เป็น iterator และคืน slice ที่ชี้เข้า buffer เดิม จึงไม่มี allocation หรือการคัดลอกครับ

size คือความกว้างหน้าต่าง ส่วน advance คือระยะเลื่อน ใช้ size=3, advance=1 สำหรับ sliding window; ใช้ค่าเท่ากันเพื่อแบ่ง chunk; หรือ size=1, advance=2 เพื่อ sampling ทุกตำแหน่งเว้นตำแหน่ง

const std = @import("std");

test "moving windows share the original sequence" {
    const readings = [_]i32{ 4, 7, 9, 12, 15 };
    var it = std.mem.window(i32, &readings, 3, 1);

    try std.testing.expectEqualSlices(i32, &.{ 4, 7, 9 }, it.next().?);
    try std.testing.expectEqualSlices(i32, &.{ 7, 9, 12 }, it.next().?);
    try std.testing.expectEqualSlices(i32, &.{ 9, 12, 15 }, it.next().?);
    try std.testing.expectEqual(null, it.next());
}

test "chunk mode keeps the final short tail" {
    const bytes = "abcdefg";
    var it = std.mem.window(u8, bytes, 3, 3);

    try std.testing.expectEqualStrings("abc", it.next().?);
    try std.testing.expectEqualStrings("def", it.next().?);
    try std.testing.expectEqualStrings("g", it.next().?);
    try std.testing.expectEqual(null, it.next());
}

จุดที่มักเข้าใจผิด

ในโหมด chunk หน้าต่างสุดท้ายอาจสั้นกว่า size ดังตัวอย่าง "g" ถ้า consumer ต้องการก้อนเต็มเท่านั้น ให้ตรวจ slice.len == size ก่อนใช้ ส่วน size และ advance ห้ามเป็นศูนย์ เพราะ library ใช้ assertion—not error union—กับสัญญานี้ จึงควร validate ค่าจาก CLI หรือ config ก่อนสร้าง iterator

slice ทุกก้อนยืม storage จาก buffer ต้นทาง อย่าเก็บไว้เกินอายุ buffer และอย่าแก้ buffer ระหว่างอ่านถ้าต้องการผลที่คงที่ ข้อดีคือเหมาะกับ hot path ที่ไม่อยากสร้าง array ใหม่ แต่ถ้าต้องส่งแต่ละหน้าต่างข้าม thread หรือเก็บระยะยาว การ copy อย่างตั้งใจอาจชัดกว่า

Recap

  • window แยกขนาดหน้าต่างออกจากระยะเลื่อน
  • iterator คืน slice ของข้อมูลเดิม ไม่ allocate
  • chunk mode อาจคืน tail ที่สั้นกว่าขนาด
  • validate ค่าศูนย์และรักษา lifetime ของ buffer ครับ