Avatar ·

Как в Go перебрать диапазон целых чисел от A до B без создания слайса?

Всем привет! Я только начал изучать Go после Python и столкнулся с неожиданной для меня проблемой. В Python я привык писать `for i in range(10, 20)`, чтобы просто перебрать числа от 10 до 19. В Go я ожидал увидеть что-то похожее, но, насколько я понял, `range` в Go работает только со слайсами, массивами, строками, картами и каналами. Мне нужно выполнить простую задачу: вывести на экран числа от 1 до 100. Я попробовал написать так: ```go for i := range 100 { fmt.Println(i) } ``` Но это не сработало — компилятор ругается, что `range` требует итератор. Тогда я попробовал сделать так: ```go for i := 0; i < 100; i++ { fmt.Println(i) } ``` Это работает, но кажется очень многословным по сравнению с Python. Получается, что в Go нет встроенного способа просто перебрать числа? Мне приходится каждый раз писать конструкцию с инициализацией, условием и инкрементом? Что если мне нужно перебрать числа от 5 до 15? Или от 100 до 1 с шагом 2? Неужели нет элегантного решения? Я читал, что в Go 1.22 появились какие-то изменения в `range`, но до конца не понял, решают ли они мою проблему. Подскажите, как правильно и красиво перебирать диапазоны чисел в Go? <<>>

Классический цикл for: ваш главный инструмент

Да, вы правы: в Go нет встроенной функции range() как в Python. Но это не недостаток, а особенность дизайна языка. Go предпочитает явность и простоту. Классический цикл for — это основной и самый идиоматичный способ перебора чисел.

Ваш пример for i := 0; i < 100; i++ — абсолютно правильный и широко используемый код. Он не многословен, а явен: вы сразу видите начальное значение, условие продолжения и шаг. Это повышает читаемость, особенно в сложных алгоритмах.

Перебор в обратном порядке и с шагом

Для перебора от 100 до 1 с шагом 2:

for i := 100; i >= 1; i -= 2 {
    fmt.Println(i)
}

Перебор от A до B

Для диапазона от 5 до 15:

for i := 5; i <= 15; i++ {
    fmt.Println(i)
}

Новое в Go 1.22: range над целыми числами

Начиная с Go 1.22 (вышел в феврале 2024 года), появилась возможность использовать range с целым числом! Теперь ваш первый пример будет работать:

for i := range 100 {
    fmt.Println(i) // выведет 0, 1, 2, ..., 99
}

Важно: range 100 перебирает числа от 0 до 99 (не включая 100), аналогично for i := 0; i < 100; i++. Это удобно, когда нужно просто выполнить действие N раз или перебрать индексы от 0 до N-1.

А как же диапазон от A до B?

Прямой поддержки range(5, 15) в Go нет и не планируется. Но вы можете использовать небольшие вспомогательные функции:

package main

import "fmt"

// Range возвращает слайс чисел от start до end (не включая end)
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, 15) {
        fmt.Println(i) // 5, 6, ..., 14
    }
}

Но! В Go принято избегать создания слайсов без необходимости. Лучше использовать классический for — он не выделяет память и работает быстрее.

Идиоматичный подход в Go

Вместо того чтобы искать аналог Python-функции range, привыкайте к явному циклу for. Это стандарт Go, который используют все опытные разработчики. Вот несколько примеров:

  • Повторить действие N раз: for i := 0; i < N; i++ { ... }
  • Перебор с конца: for i := N-1; i >= 0; i-- { ... }
  • Бесконечный цикл: for { ... }
  • Цикл с условием: for i < 10 { ... } (как while в других языках)

Примите эту особенность Go — она делает код предсказуемым и эффективным. А если вам очень не хватает Python-стиля, используйте range N из Go 1.22 для простых случаев.

<<>>

Альтернатива: итераторы и функциональный подход

Если вы хотите более выразительный код и не боитесь небольших накладных расходов на производительность, можно создать функцию-генератор, возвращающую итератор. Начиная с Go 1.23 (ожидается в 2024 году), в язык будут добавлены встроенные итераторы (iterators). Но уже сейчас можно использовать простой паттерн с замыканиями:

func rangeInt(start, end int) func() (int, bool) {
    i := start
    return func() (int, bool) {
        if i >= end {
            return 0, false
        }
        val := i
        i++
        return val, true
    }
}

func main() {
    next := rangeInt(5, 15)
    for {
        val, ok := next()
        if !ok {
            break
        }
        fmt.Println(val)
    }
}

Этот подход более гибкий: вы можете легко изменить шаг или добавить фильтрацию. Однако для простых задач классический for остаётся лучшим выбором. Помните: в Go простота и производительность важнее синтаксического сахара.