Как в Go организовать цикл по диапазону целых чисел без использования for i := 0; i < N; i++?
Краткий ответ
В Go нет встроенной конструкции for i in range(N) как в Python, но есть несколько идиоматичных способов, которые делают код чище. Самый простой и рекомендуемый — использовать классический for, но с пустыми выражениями, если вам нужно только количество итераций.
Способ 1: Классический for с пустым телом (для N итераций)
Если вам нужно просто выполнить действие N раз, не используя индекс, можно опустить переменную цикла:
package main
import "fmt"
func main() {
n := 5
for i := 0; i < n; i++ {
fmt.Println("Iteration", i)
}
}
Это стандартный и самый производительный способ. Не пытайтесь его избегать — он идиоматичен для Go.
Способ 2: Использование range с каналом (Go 1.22+)
Начиная с Go 1.22, можно использовать for range с целым числом:
package main
import "fmt"
func main() {
for i := range 5 {
fmt.Println(i) // 0, 1, 2, 3, 4
}
}
Это нововведение очень похоже на Python. Важно: range 5 дает числа от 0 до 4. Если вам нужен диапазон [A, B), используйте:
for i := range 10 {
if i >= 5 {
fmt.Println(i) // 5, 6, 7, 8, 9
}
}
Но это не очень эффективно. Лучше использовать классический for.
Способ 3: Функция-генератор (для сложных диапазонов)
Если вам часто нужны нестандартные диапазоны (с шагом, от A до B), можно написать вспомогательную функцию:
package main
import "fmt"
func Range(start, end int) []int {
if start > end {
return nil
}
result := make([]int, end-start)
for i := start; i < end; i++ {
result[i-start] = i
}
return result
}
func main() {
for _, i := range Range(5, 10) {
fmt.Println(i) // 5, 6, 7, 8, 9
}
}
Недостаток: создается срез, что может быть неэффективно для больших диапазонов.
Способ 4: Канал для ленивой генерации
Для больших диапазонов лучше использовать канал:
package main
import "fmt"
func RangeChan(start, end int) <-chan int {
ch := make(chan int)
go func() {
for i := start; i < end; i++ {
ch <- i
}
close(ch)
}()
return ch
}
func main() {
for i := range RangeChan(5, 10) {
fmt.Println(i) // 5, 6, 7, 8, 9
}
}
Этот вариант ленивый и не выделяет память под весь диапазон, но добавляет накладные расходы на горутину и канал.
Рекомендации
- Для простых диапазонов (0..N): используйте
for i := 0; i < N; i++илиfor i := range N(Go 1.22+). - Для диапазонов [A..B): используйте
for i := A; i < B; i++. - Для шага, отличного от 1:
for i := A; i < B; i += step. - Избегайте создания срезов для больших диапазонов — это неэффективно по памяти.
Помните: в Go простота и производительность важнее синтаксического сахара. Классический for — это не костыль, а идиома языка.
Альтернативный подход: Использование пакета lo (дженерики)
Если вы используете Go 1.18+ и хотите писать в функциональном стиле, можно применить библиотеку github.com/samber/lo, которая предоставляет множество утилит, включая lo.Range:
package main
import (
"fmt"
"github.com/samber/lo"
)
func main() {
// Генерирует срез [0, 1, 2, 3, 4]
for _, i := range lo.Range(5) {
fmt.Println(i)
}
// Генерирует срез [2, 3, 4, 5, 6] с шагом 1
for _, i := range lo.RangeFrom(2, 5) {
fmt.Println(i)
}
// Генерирует срез [0, 2, 4] с шагом 2
for _, i := range lo.RangeWithSteps(0, 5, 2) {
fmt.Println(i)
}
}
Этот подход удобен для цепочек операций (map, filter), но помните: lo.Range создает срез в памяти, поэтому для очень больших диапазонов (миллионы элементов) лучше использовать классический for или канал.
Если вы не хотите добавлять внешнюю зависимость, просто используйте классический for — это самый производительный и понятный способ в Go.
Ответов пока нет.
Войдите чтобы оставить ответ