Go1.23 ได้นำฟีเจอร์ range-over-func ที่เป็น experiment ใน go1.22 มาให้ใช้จริง ซึ่งถ้าใครได้อ่านโค้ดตัวอย่างเข้าไปแล้วอาจจะต้องนั่งสมาธิกันนานหน่อย อย่ากระนั้นเลย พี่ยอดจะมาอธิบายแบบง่ายสุดๆให้อ่านกัน
เริ่มจาก spec ของ For statements with range clause ได้เพิ่มเติม Expression เข้ามา 3 แบบคือ
func(func() bool)
func(func(V) bool)
func(func(K, V) bool)
โดยพี่จะขอเพิ่มตัวแปรให้ตัวนึงเพิ่อจะได้อธิบายดังนี้
f func(yield func() bool)
f func(yield func(V) bool)
f func(yield func(K, V) bool)
ใน spec บอกว่า เมื่อเราใช้ฟังก์ชั่น f ไปเป็น expression ใน range ทุกครั้งที่เราเรียกฟังก์ชั่น yield ในนั้นก่อนจะจบฟังก์ชั่น f เราจะได้ผลลัพธ์ในแต่ละลูป เท่ากับค่าที่เราใส่เข้าไปใน yield อธิบายแล้วก็ยังงง เขียนโค้ดเลยดีกว่า
func main() {
for range loop {
fmt.Println("-")
}
}
func loop(yield func() bool) {
yield()
yield()
}
output:
-
-
ถ้าเราเขียนโค้ดแบบนี้ เราจะได้ loop 2 รอบถ้วน เพราะเราเรียก yield 2 ครั้งใน f ตาม spec ซึ่งในที่นี่เราตั้งชื่อว่า loop
และมันจะไม่คืนค่าอะไรออกมาให้เรา เพราะเราเลือกใช้ pattern ที่ yield ไม่รับ arguments ใดๆเลย
อีกตัวอย่าง
func main() {
for i := range loop {
fmt.Println(i)
}
}
func loop(yield func(int) bool) {
yield(3)
yield(7)
}
output:
3
7
แบบนี้เราจะได้ 2 รอบเหมือนกัน เพราะเราเรียก yield ครั้ง และทีนี้ range จะคืนค่ามาให้ 2 ค่า ซึ่งก็คือ 3 และ 7 ที่เราใช้เรียก yield ในแต่ละครั้ง
อีกตัวอย่าง
func main() {
for k, v := range loop {
fmt.Println(k, v)
}
}
func loop(yield func(int, string) bool) {
yield(3, "three")
yield(5, "five")
yield(7, "seven")
}
output:
3 three
5 five
7 seven
เราก็จะได้ loop 3 รอบ และได้ค่าออกมารอบละ 2 ค่าตามที่เราใส่ให้ yield ในแต่ละครั้ง
และเรายังสามารถเรียก yield ด้วยการใส่ arguments แบบไหนก็ได้เช่น
func loop(yield func(string, bool) bool) {
yield("three", true)
yield("five", false)
yield("seven", false)
}
ทีนี้พอเราเข้าใจกลไกลของมันแล้ว เวลาเราไปอ่านตัวอย่างยากๆเราจะเข้าใจมากขึ้นเช่นตัวอย่างใน Go Wiki: Rangefunc Experiment
package slices
func Backward[E any](s []E) func(func(int, E) bool) {
return func(yield func(int, E) bool) {
for i := len(s)-1; i >= 0; i-- {
if !yield(i, s[i]) {
return
}
}
}
}
main
s := []string{"hello", "world"}
for i, x := range slices.Backward(s) {
fmt.Println(i, x)
}
อ่านง่ายขึ้นเลยใช่ไหม สุดท้ายจะเอาไปประยุกต์ยังไงก็แล้วแต่ เราดูแค่ตอนที่เรียก yield ว่าเรียกกี่รอบ ก็จะได้เท่านั้นรอบตอนเอาไปใส่ใน range
ส่วนค่าที่จะได้ออกมาก็คือค่าที่หย่อนลงไปใน yield นั่นแหล่ะ จบ
Top comments (0)