> For the complete documentation index, see [llms.txt](https://eda-1.gitbook.io/lgwt/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://eda-1.gitbook.io/lgwt/osnovy-go/iteration.md).

# Итерация

[**Весь код для этой главы вы найдете здесь**](https://github.com/quii/learn-go-with-tests/tree/main/for)

Чтобы выполнять действия многократно в Go, вам понадобится `for`. В Go нет ключевых слов `while`, `do`, `until`, вы можете использовать только `for`. Что очень хорошо!

Давайте напишем тест для функции, которая повторяет символ 5 раз.

Пока ничего нового, так что попробуйте написать его самостоятельно для практики.

## Сначала напишите тест

```go
package iteration

import "testing"

func TestRepeat(t *testing.T) {
	repeated := Repeat("a")
	expected := "aaaaa"

	if repeated != expected {
		t.Errorf("expected %q but got %q", expected, repeated)
	}
}
```

## Попробуйте запустить тест

`./repeat_test.go:6:14: undefined: Repeat`

## Напишите минимальный объем кода, чтобы тест запустился и проверьте вывод неудачного теста

*Соблюдайте дисциплину!* Вам не нужно знать ничего нового прямо сейчас, чтобы тест корректно завершился неудачей.

Всё, что вам нужно сделать сейчас, это обеспечить компиляцию, чтобы вы могли убедиться, что ваш тест написан хорошо.

```go
package iteration

func Repeat(character string) string {
	return ""
}
```

Разве не приятно знать, что вы уже достаточно хорошо знаете Go, чтобы писать тесты для некоторых базовых задач? Это означает, что теперь вы можете работать с продакшн-кодом столько, сколько захотите, и знать, что он ведет себя так, как вы ожидаете.

`repeat_test.go:10: expected 'aaaaa' but got ''`

## Напишите достаточно кода, чтобы тест прошел

Синтаксис `for` очень прост и соответствует большинству C-подобных языков.

```go
func Repeat(character string) string {
	var repeated string
	for i := 0; i < 5; i++ {
		repeated = repeated + character
	}
	return repeated
}
```

В отличие от других языков, таких как C, Java или JavaScript, нет круглых скобок, окружающих три компонента оператора `for`, и фигурные скобки `{ }` всегда обязательны. Вы можете задаться вопросом, что происходит в строке

```go
	var repeated string
```

поскольку до сих пор мы использовали `:=` для объявления и инициализации переменных. Однако `:=` — это просто [сокращенная запись обоих шагов](https://gobyexample.com/variables). Здесь мы объявляем только переменную типа `string`. Отсюда и явная версия. Мы также можем использовать `var` для объявления функций, как мы увидим позже.

Запустите тест, и он должен пройти.

Дополнительные варианты цикла `for` описаны [здесь](https://gobyexample.com/for).

## Рефакторинг

Теперь пришло время для рефакторинга и введения другой конструкции: оператора присваивания `+=`.

```go
const repeatCount = 5

func Repeat(character string) string {
	var repeated string
	for i := 0; i < repeatCount; i++ {
		repeated += character
	}
	return repeated
}
```

`+=` называется *«оператором сложения И присваивания»* (Add AND assignment operator), он добавляет правый операнд к левому операнду и присваивает результат левому операнду. Он работает и с другими типами, такими как целые числа.

### Бенчмаркинг

Написание [бенчмарков](https://golang.org/pkg/testing/#hdr-Benchmarks) в Go — еще одна первоклассная особенность языка, и это очень похоже на написание тестов.

```go
func BenchmarkRepeat(b *testing.B) {
	for b.Loop() {
		Repeat("a")
	}
}
```

Вы увидите, что код очень похож на тест.

`testing.B` предоставляет вам доступ к функции цикла. `Loop()` возвращает `true` до тех пор, пока бенчмарк должен продолжать выполняться.

Когда код бенчмарка выполняется, он измеряет время его работы. После того как `Loop()` возвращает `false`, `b.N` содержит общее количество выполненных итераций.

Количество запусков кода не должно иметь для вас значения, фреймворк сам определит «хорошее» значение, чтобы вы получили достойные результаты.

Для запуска бенчмарков выполните `go test -bench=.` (или, если вы используете Windows Powershell, `go test -bench="."`)

```
goos: darwin
goarch: amd64
pkg: github.com/quii/learn-go-with-tests/for/v4
10000000           136 ns/op
PASS
```

Что означает `136 ns/op`: наша функция в среднем выполняется 136 наносекунд (на моем компьютере). Что вполне нормально! Для проверки она была запущена 10 000 000 раз.

**Примечание:** По умолчанию бенчмарки выполняются последовательно.

Измеряется только тело цикла; код установки и очистки автоматически исключается из замера времени бенчмарка. Типичный бенчмарк имеет следующую структуру:

```go
func Benchmark(b *testing.B) {
	//... setup ...
	for b.Loop() {
		//... code to measure ...
	}
	//... cleanup ...
}
```

Строки в Go неизменяемы, что означает, что каждая конкатенация, как в нашей функции `Repeat`, включает копирование памяти для размещения новой строки. Это влияет на производительность, особенно при интенсивной конкатенации строк.

Стандартная библиотека предоставляет тип `strings.Builder`[stringsBuilder](https://pkg.go.dev/strings#Builder), который минимизирует копирование памяти. Он реализует метод `WriteString`, который мы можем использовать для конкатенации строк:

```go
const repeatCount = 5

func Repeat(character string) string {
	var repeated strings.Builder
	for i := 0; i < repeatCount; i++ {
		repeated.WriteString(character)
	}
	return repeated.String()
}
```

**Примечание**: Мы должны вызвать метод `String`, чтобы получить конечный результат.

Мы можем использовать `BenchmarkRepeat`, чтобы подтвердить, что `strings.Builder` значительно повышает производительность. Запустите `go test -bench=. -benchmem`:

```
goos: darwin
goarch: amd64
pkg: github.com/quii/learn-go-with-tests/for/v4
10000000           25.70 ns/op           8 B/op           1 allocs/op
PASS
```

Флаг `-benchmem` выводит информацию о выделении памяти:

* `B/op`: количество выделенных байтов за итерацию
* `allocs/op`: количество выделений памяти за итерацию

## Практические упражнения

* Измените тест так, чтобы вызывающая сторона могла указывать, сколько раз повторяется символ, а затем исправьте код.
* Напишите `ExampleRepeat` для документирования вашей функции.
* Просмотрите пакет [strings](https://golang.org/pkg/strings). Найдите функции, которые, по вашему мнению, могут быть полезны, и поэкспериментируйте с ними, написав тесты, как мы делали здесь. Инвестирование времени в изучение стандартной библиотеки со временем окупится.

## Итоги

* Больше практики TDD
* Изучили `for`
* Научились писать бенчмарки

***


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://eda-1.gitbook.io/lgwt/osnovy-go/iteration.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
