Зарегистрируйтесь, чтобы продолжить обучение

Функции как аргумент Go: Функции

Когда мы говорим «функция как аргумент», имеется в виду, что функция может быть передана внутрь другой функции так же, как мы обычно передаём число, строку или структуру. В Go это возможно потому, что функция — это значение, полноценный объект. У неё есть тип, и этот тип можно указать в параметрах.

Почему это важно? Представьте: у нас есть алгоритм, который одинаков для многих задач. Но внутри есть кусок логики, который зависит от ситуации. Если делать копию функции для каждого случая — будет куча дублирования. А если вынести различающуюся часть наружу и передавать её как функцию, код станет компактным и гибким.

Пример

package main

import "fmt"

// greet принимает строку (имя) и функцию, которая умеет печатать сообщение
func greet(name string, printer func(string)) {
    message := "Привет, " + name
    printer(message) // вызываем переданную функцию
}

func main() {
    // Передаём готовую функцию fmt.Println
    greet("Аня", fmt.Println)
    // Вывод: Привет, Аня

    // Передаём анонимную функцию, которая печатает в другом формате
    greet("Ваня", func(s string) {
        fmt.Println("***", s, "***")
    })
    // Вывод: *** Привет, Ваня ***
}

Здесь greet сама не знает, как именно выводить сообщение. Она лишь формирует строку и передаёт её в функцию printer, которую мы задаём как аргумент. Хотим печатать просто — передаём fmt.Println. Хотим добавить звёздочки — пишем анонимную функцию.

Пример: калькулятор

package main

import "fmt"

// calculate принимает два числа и операцию над ними
func calculate(a, b int, op func(int, int) int) int {
    return op(a, b)
}

func main() {
    // Определим разные операции
    add := func(x, y int) int { return x + y }
    mul := func(x, y int) int { return x * y }

    fmt.Println(calculate(3, 4, add)) // 7
    fmt.Println(calculate(3, 4, mul)) // 12

    // Можно сразу написать операцию в вызове
    result := calculate(10, 5, func(x, y int) int {
        return x - y
    })
    fmt.Println(result) // 5
}

Функция calculate универсальна: она не знает, что такое «сложение» или «умножение». Она просто принимает два числа и вызывает переданную операцию. Это и есть сила функций как аргументов: мы описываем общий каркас, а детали подставляем на ходу.

Пример: фильтрация

Фильтрация — классика. Есть список значений, и нужно оставить только те, которые удовлетворяют условию.

package main

import "fmt"

// filter оставляет только элементы, которые проходят проверку
func filter(nums []int, check func(int) bool) []int {
    var result []int
    for _, n := range nums {
        if check(n) {
            result = append(result, n)
        }
    }
    return result
}

func main() {
    numbers := []int{1, 2, 3, 4, 5, 6}

    // Оставляем только чётные
    evens := filter(numbers, func(x int) bool { return x%2 == 0 })
    fmt.Println(evens) // [2 4 6]

    // Оставляем числа больше трёх
    greater := filter(numbers, func(x int) bool { return x > 3 })
    fmt.Println(greater) // [4 5 6]
}

Здесь filter один для всех случаев. Вынесли переменную часть — «условие» — в функцию check. Теперь её можно подставлять разную: хоть проверку на чётность, хоть проверку на диапазон, хоть на простое число.

Объяснение на пальцах

Можно думать так: когда мы пишем функцию с функцией-аргументом, мы словно оставляем пустое место — «дырку» — и говорим: «Заполню её позже нужной логикой».

Например:

  • В calculate пустое место — это операция.
  • В filter пустое место — это условие.
  • В greet пустое место — это способ вывести сообщение. \

Ещё пример: обработчики событий

Это часто встречается в веб-серверах и GUI.

package main

import "fmt"

// onEvent принимает строку события и обработчик
func onEvent(event string, handler func(string)) {
    fmt.Println("Событие:", event)
    handler(event)
}

func main() {
    onEvent("click", func(e string) {
        fmt.Println("Обрабатываем:", e)
    })

    onEvent("hover", func(e string) {
        fmt.Println("Навели мышку:", e)
    })
}

Функция onEvent не знает, что такое «клик» или «наведение». Ей всё равно. Она просто вызывает переданный обработчик. Это позволяет строить гибкие системы, где логика задаётся снаружи.

Функции как аргументы нужны, чтобы отделять общий алгоритм от частных деталей. Это убирает дублирование, делает код более выразительным и расширяемым. Алгоритм описывается один раз, а поведение можно менять, подставляя разные функции.


Самостоятельная работа

Потренируемся в создании функции, которая принимает на вход другие функции.

Напишите функцию transform, которая принимает срез строк и функцию func(string) string, преобразующую каждую строку. Верните новый срез с результатами применения этой функции.

Используйте её для обработки списка имён, передав анонимную функцию, которая:

  • обрезает пробелы по краям,
  • переводит имя в верхний регистр,
  • и добавляет восклицательный знак в конце.

Пример использования:

names := []string{"  alice ", "Bob", "  charlie "}
res := transform(names, func(s string) string {
    // ...
})
fmt.Println(res) // [ALICE! BOB! CHARLIE!]

Попробуйте также вызвать transform с другой анонимной функцией — например, чтобы посчитать длину строк.

Показать решение
package main

import (
    "fmt"
    "strings"
)

func transform(xs []string, f func(string) string) []string {
    res := make([]string, len(xs))
    for i, v := range xs {
        res[i] = f(v)
    }
    return res
}

func main() {
    names := []string{"  alice ", "Bob", "  charlie "}
    excited := transform(names, func(s string) string {
        s = strings.TrimSpace(s)
        s = strings.ToUpper(s)
        return s + "!"
    })
    fmt.Println(excited) // [ALICE! BOB! CHARLIE!]
}

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff