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 не знает, что такое «клик» или «наведение». Ей всё равно. Она просто вызывает переданный обработчик. Это позволяет строить гибкие системы, где логика задаётся снаружи.

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

Рекомендуемые программы

+7 800 100 22 47

бесплатно по РФ

+7 495 085 21 62

бесплатно по Москве

108813 г. Москва, вн.тер.г. поселение Московский,
г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3
ОГРН 1217300010476
ИНН 7325174845