🐹 Go

Функции и рекурсия в Go 1.21: подробное руководство

Функции и рекурсия в Go

Введение: зачем нужны функции?

Представь, что ты учишься печь пирожки. Каждый раз, когда тебе нужно испечь пирожок, ты не изобретаешь заново весь процесс: берёшь тесто, лепишь, ставишь в духовку. Ты просто используешь готовый рецепт.

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

Когда функция вызывает саму себя — это называется рекурсия. Это как матрёшка: внутри большой матрёшки лежит поменьше, внутри неё — ещё меньше, и так до самой маленькой.

Основной синтаксис функций в Go

В Go функция начинается со слова func, затем идёт её имя, в скобках — параметры (входные данные), а после скобок — тип возвращаемого значения.

// Простая функция без параметров
func sayHello() {
    fmt.Println("Привет, мир!")
}

// Функция с параметрами и возвратом значения
func sum(a int, b int) int {
    return a + b
}

// Функция с двумя возвращаемыми значениями
func divide(a, b int) (int, int) {
    return a / b, a % b // возвращаем частное и остаток
}

Пример 1: Семейный бюджет (сумма двух чисел)

Напишем программу, которая принимает два числа (доходы) и выводит их сумму.

package main

import "fmt"

// Функция для подсчёта суммы доходов
func sumIncome(a float64, b float64) float64 {
    return a + b
}

func main() {
    var income1, income2 float64

    fmt.Print("Введите первый доход: ")
    fmt.Scan(&income1)

    fmt.Print("Введите второй доход: ")
    fmt.Scan(&income2)

    total := sumIncome(income1, income2)
    fmt.Printf("Общая сумма доходов: %.2f\n", total)
}

Как это работает:

  • Мы объявили функцию sumIncome, которая принимает два числа и возвращает их сумму.
  • В main мы считываем числа с клавиатуры, вызываем функцию и выводим результат.

Пример 2: Сравнение температур

Функция принимает две температуры и возвращает большую из них.

package main

import "fmt"

// Функция для поиска максимума
func maxTemperature(t1 float64, t2 float64) float64 {
    if t1 > t2 {
        return t1
    }
    return t2
}

func main() {
    var temp1, temp2 float64

    fmt.Print("Введите первую температуру: ")
    fmt.Scan(&temp1)

    fmt.Print("Введите вторую температуру: ")
    fmt.Scan(&temp2)

    result := maxTemperature(temp1, temp2)
    fmt.Printf("Большая температура: %.1f\n", result)
}

Пример 3: Проверка чётности (возврат bool)

Функция возвращает true, если число чётное, и false, если нечётное.

package main

import "fmt"

// Функция проверки чётности
func isEven(n int) bool {
    return n%2 == 0
}

func main() {
    var boxes int

    fmt.Print("Сколько коробок произведено? ")
    fmt.Scan(&boxes)

    if isEven(boxes) {
        fmt.Println("Число чётное — можно упаковывать в пары!")
    } else {
        fmt.Println("Число нечётное — одна коробка останется без пары.")
    }
}

Рекурсия: когда функция вызывает саму себя

Рекурсия удобна для задач, которые можно разбить на такие же, но более маленькие подзадачи.

Важное правило: у рекурсии всегда должно быть условие выхода (базовый случай), иначе программа зациклится.

Пример 4: Реверс строки (рекурсия)

Развернём строку задом наперёд с помощью рекурсии.

package main

import "fmt"

// Рекурсивная функция для реверса строки
func reverseString(s string) string {
    // Базовый случай: если строка пустая или из одного символа
    if len(s) <= 1 {
        return s
    }
    // Берём последний символ + рекурсивно разворачиваем остаток
    return string(s[len(s)-1]) + reverseString(s[:len(s)-1])
}

func main() {
    var text string

    fmt.Print("Введите строку: ")
    fmt.Scan(&text)

    result := reverseString(text)
    fmt.Println("Результат:", result)
}

Как это работает:

  • Если строка из одного символа — возвращаем её (базовый случай).
  • Иначе берём последний символ и приклеиваем к нему развёрнутую строку без последнего символа.

Пример 5: Поиск минимального элемента в списке (рекурсия)

package main

import "fmt"

// Рекурсивная функция для поиска минимума
func findMin(arr []int, n int) int {
    // Базовый случай: если в массиве один элемент
    if n == 1 {
        return arr[0]
    }
    // Находим минимум в меньшей части массива
    min := findMin(arr, n-1)
    // Сравниваем с последним элементом
    if arr[n-1] < min {
        return arr[n-1]
    }
    return min
}

func main() {
    // Пример данных: температуры за сутки
    temperatures := []int{-5, 3, -8, 12, 0, 7, -2}

    minTemp := findMin(temperatures, len(temperatures))
    fmt.Printf("Самая низкая температура: %d°C\n", minTemp)
}

Частые ошибки новичков

  1. Забывают указать тип возвращаемого значения — если функция что-то возвращает, нужно обязательно написать тип после скобок.
  2. Путают параметры и аргументы — параметры это переменные в определении функции, аргументы — конкретные значения при вызове.
  3. Бесконечная рекурсия — забывают написать условие выхода. Программа будет работать, пока не закончится память.
  4. Используют return не во всех ветках — если функция возвращает значение, return должен быть во всех случаях (и в if, и в else).
  5. Пытаются изменить параметры внутри функции — в Go параметры передаются по значению (копируются). Изменение параметра не влияет на оригинальную переменную.

Итоговый пример: "Калькулятор скидки в магазине"

Напишем программу, которая рассчитывает цену со скидкой. Если сумма покупки больше 1000 рублей — скидка 10%, иначе — 5%.

package main

import "fmt"

// Функция для расчёта скидки
func calculateDiscount(price float64) float64 {
    if price > 1000 {
        return price * 0.9 // скидка 10%
    }
    return price * 0.95 // скидка 5%
}

// Рекурсивная функция для подсчёта суммы нескольких покупок
func totalWithDiscount(prices []float64, index int) float64 {
    if index == len(prices) {
        return 0
    }
    return calculateDiscount(prices[index]) + totalWithDiscount(prices, index+1)
}

func main() {
    // Список покупок
    purchases := []float64{500, 1200, 300, 1500}

    total := totalWithDiscount(purchases, 0)
    fmt.Printf("Итоговая сумма со скидками: %.2f руб.\n", total)
}

Что мы сделали:

  • Функция calculateDiscount — обычная функция для расчёта скидки на одну покупку.
  • Функция totalWithDiscount — рекурсивная, проходит по всем покупкам и суммирует их со скидкой.
  • Базовый случай: когда дошли до конца списка — возвращаем 0.

Теперь ты знаешь, как писать функции и использовать рекурсию в Go! Пробуй решать задачи — чем больше практики, тем быстрее запомнишь.