อิกัวนาต้นฉบับกำลังหมุนถาดแบ่งช่องสีเขียวให้ข้อมูลสองช่องหน้าเลื่อนไปท้าย

Zig std.mem.rotate: หมุน slice in-place สำหรับคิวแบบวงแหวน

พี่โชว์Engineering

เมื่ออยากย้ายสมาชิกช่วงหน้าไปท้ายโดยยังใช้ buffer เดิม std.mem.rotate หมุน slice แบบ in-place ได้ครับ เหมือนหมุนถาดอาหารวงกลมให้จานที่อยู่หน้าเราเลื่อนไปอีกฝั่ง โดยไม่ยกจานทั้งหมดไปใส่ถาดใหม่

HOW: amount คือจำนวนสมาชิกที่เลื่อนไปทางซ้าย

const std = @import("std");

test "rotate queue left in place" {
    var queue = [_]u8{ 10, 20, 30, 40 };

    std.mem.rotate(u8, &queue, 2);

    try std.testing.expectEqualSlices(u8, &.{ 30, 40, 10, 20 }, &queue);
}

test "zero rotation keeps the slice" {
    var queue = [_]u8{ 1, 2, 3 };
    std.mem.rotate(u8, &queue, 0);
    try std.testing.expectEqualSlices(u8, &.{ 1, 2, 3 }, &queue);
}

ฟังก์ชันนี้แก้ข้อมูลจริง จึงเหมาะกับงาน batch ที่ต้องส่ง slice ต่อในลำดับใหม่ทันที เช่น เลื่อนงานที่ประมวลผลแล้วไปท้ายรอบ หรือทำ canonical rotation ก่อนเปรียบเทียบ แต่ถ้าเป็น ring buffer ที่หมุนทุก request การขยับสมาชิกซ้ำอาจแพงกว่าการเก็บ head index แล้วคำนวณตำแหน่งด้วย modulo

ใน Zig 0.16 caller ต้องให้ amount <= items.len ตามสัญญาของ API อย่าส่งค่าจากผู้ใช้ตรง ๆ ควร normalize ด้วย % items.len หลังตรวจว่า slice ไม่ว่าง เพราะ modulo ศูนย์ก็เป็น bug อีกแบบหนึ่งครับ

Recap

  • amount ย้ายสมาชิกช่วงหน้าไปท้าย
  • ฟังก์ชันแก้ slice เดิมและไม่ allocate
  • normalize ค่า input ก่อนเรียก โดยแยกกรณี slice ว่าง
  • ring buffer ที่หมุนบ่อยควรใช้ head index แทนการย้ายข้อมูล