กระรอกดินต้นฉบับเดินเก็บของจากท้ายถาดมาหน้าตามลูกศรย้อน โดยของในถาดเดิมไม่ถูกสลับ

Go slices.Backward: วนจากท้ายโดยไม่กลับลำดับข้อมูลต้นฉบับ

พี่โชว์Engineering

บางงานต้องหาค่าล่าสุดก่อน เช่น event ล่าสุดที่ผ่านเงื่อนไข แต่ไม่อยากกลับลำดับ slice เพราะข้อมูลต้นฉบับยังถูกใช้ที่อื่นครับ slices.Backward คืน iterator ที่เดินคู่ index, value จากท้ายมาหน้าโดยไม่แก้ slice

WHY: แยก “ลำดับที่อ่าน” ออกจาก “ลำดับที่เก็บ”

การเรียก slices.Reverse เปลี่ยนข้อมูลจริง เหมือนสลับแฟ้มทั้งตู้เพื่อหยิบแฟ้มหลังสุด ส่วน Backward เหมือนเราเดินอ้อมไปเปิดตู้จากอีกด้าน แฟ้มยังอยู่ที่เดิม โค้ดที่ถือ slice เดียวกันจึงไม่เห็นผลข้างเคียง

HOW: range รับ index กับค่าได้ตรง ๆ

package main

import (
	"fmt"
	"slices"
)

func main() {
	values := []int{10, 20, 30}
	got := make([]int, 0, len(values))
	indexes := make([]int, 0, len(values))

	for index, value := range slices.Backward(values) {
		indexes = append(indexes, index)
		got = append(got, value)
	}

	if !slices.Equal(got, []int{30, 20, 10}) {
		panic(got)
	}
	if !slices.Equal(indexes, []int{2, 1, 0}) {
		panic(indexes)
	}
	if !slices.Equal(values, []int{10, 20, 30}) {
		panic("input was mutated")
	}
	fmt.Println(got)
}

Backward คืน iter.Seq2 จึงเป็นการดึงค่าทีละคู่ ไม่ได้สร้าง slice กลับด้านอีกก้อน ถ้าต้องเก็บผลย้อนจริงค่อย append เหมือนตัวอย่าง แต่ถ้าแค่หาเจอแล้ว break จะหยุดได้ทันทีและไม่เสียหน่วยความจำตามขนาดข้อมูล

ข้อควรระวังคืออย่าแก้โครงสร้าง slice ระหว่างเดิน iterator เพราะ index ที่กำลังใช้ผูกกับ snapshot ของ header ตอนเริ่มอ่าน ถ้าต้องลบหรือย้ายสมาชิก ให้เก็บ index ที่พบแล้วค่อยแก้หลัง loop

Recap

  • ใช้ Backward เมื่อต้องอ่านท้ายไปหน้าแต่ไม่อยาก mutate
  • รับทั้ง index และ value ได้จาก range
  • หยุดเร็วด้วย break ได้โดยไม่สร้างสำเนา
  • แยกช่วงค้นหาออกจากช่วงแก้ slice