Введение
В предыдущей части цикла мы рассмотрели ключевое слово defer в golang, в этом разделе мы разберем, как можно использовать анонимные функции в golang. Мы рассмотрим, как объявлять и использовать анонимные функции на нескольких примерах.
Что такое анонимные функции
Анонимные функции довольно просты для понимания, мы не определяем функцию, мы объявляем ее и тут же вызываем. У анонимной функции нет имени, поэтому она называется анонимной. Как и обычная функция, она может принимать параметры и возвращать значения. С помощью анонимных функций мы можем привязать операции к переменной или константе в виде литерала (значения). Если анонимная функция принимает параметр, он должен быть разобран сразу после конца тела функции. Мы рассмотрим, как определить синтаксис и спецификации анонимных функций в golang.
Простые анонимные функции
Для создания простой анонимной функции мы используем тот же синтаксис функции, не давая ей имени, например func() {}
, внутри тела функции, то есть {}
, вы можете определить операции, которые необходимо выполнить.
Здесь мы создали анонимную функцию, которая просто вызывает функцию fmt.Println
со строкой. Таким образом, мы немного перестарались, поскольку мы даже можем напрямую вызвать функцию fmt.Println
из основной функции, вместо этого мы вызвали анонимную функцию, которая в свою очередь вызывает функцию fmt.Println
. Возможно, использовать анонимную функцию здесь не имеет смысла, но она может быть полезна для других сложных задач, в которых необходимо изолировать логику без создания специальной функции для этого процесса.
package main
import "fmt"
func main() {
func() {
fmt.Println("Hello, Anonymous functions")
}()
}
go run anonymous_function.go
Hello, Anonymous functions
Итак, вот как мы создаем базовую анонимную функцию в golang, которая в дальнейшем может быть использована для возврата значений из функции или передачи параметров в функцию, а также для присвоения вызова функции переменной или константе.
Присвоение анонимной функции переменным
Мы можем присвоить вызов анонимной функции переменной или константе и вызывать функцию столько раз, сколько нам нужно. Таким образом, мы можем хранить логику функции в переменной и вызывать ее всякий раз, когда нам нужна функция, используя скобку ()
как указание на вызов функции.
В следующем примере мы использовали переменную draw
для хранения вызова функции, которая печатает Drawing
с помощью функции fmt.Println
. Теперь переменная draw содержит функцию, а не ее значение. Поэтому будьте внимательны, анонимная функция, которую мы определили как буквальное значение переменной draw
, как будто мы даем имя этой анонимной функции, и имя будет именем переменной, поэтому мы создали функцию draw
, которая является анонимной функцией, хранящейся в переменной.
package main
import "fmt"
func main() {
draw := func() {
fmt.Println("Drawing")
}
draw()
draw()
}
go run anonymous_function.go
Drawing
Drawing
Мы видим, что вызываем переменную draw
как вызов функции, добавляя к ней скобку ()
в виде draw()
, тем самым вызывая анонимную функцию, хранящуюся в значении переменной.
Главное, что здесь нужно отметить, это то, что мы не добавляем ()
во время объявления анонимной функции, так как она будет вызывать функцию напрямую. Проблема возникнет потому, что функция ничего не возвращает, поэтому мы не можем присвоить ее переменной.
draw := func() {
fmt.Println("Drawing")
}()
functions/anonymous_functions.go:10:11: func() {…}() (no value) used as value
Итак, мы пытаемся присвоить переменную вызову функции, которая не имеет возвращаемого значения. У нее нет никакого значения, даже nil, поэтому мы получаем ошибку присвоения переменной ничего.
Хотя если у вас есть возвращаемое значение функции, мы можем напрямую присвоить вызов функции переменной, так как он вернул действительное значение, определенное в литерале функции.
Разбор параметров
Мы можем даже разбирать параметры анонимных функций, как и любой другой обычной функции. Мы определяем имя переменной, за которым в круглой скобке следует тип переменной, чтобы использовать эти параметры внутри анонимной функции.
Необходимо помнить, что эти параметры функции могут передаваться либо вместе с вызовом переменной, либо непосредственно во время определения функции.
В примере ниже мы создали переменную call
и присвоили ее анонимной функции, которая принимает параметр name
, являющийся string
, и печатает некоторый текст на консоли. Таким образом, мы вызываем функцию call
с параметром в виде строки, поскольку у нас есть "Meet"
и person := "Chris"
в качестве строки, переданной функции call
.
package main
import "fmt"
func main() {
call := func(name string) {
fmt.Println("Calling,", name)
}
call("Meet")
person := "Chris"
call(person)
}
go run anonymous_function.go
Calling, Meet
Calling, Chris
Здесь мы видим, что функция call
печатает текст, который мы определили для вызова функции fmt.Println
. Мы разбираем функцию с помощью строкового литерала, поскольку анонимная функция принимает параметр в виде строки. Мы можем анализировать несколько параметров анонимной функции, как и обычной функции, например, slices, maps, arrays, struct и т.д.
Возвращение значений
Мы можем даже возвращать значения из анонимной функции, если хотим мгновенно вызвать функцию и сохранить значение returned
в переменной. Мы можем возвращать одно или несколько значений в соответствии с нашими требованиями, как и любая обычная функция в golang.
package main
import "fmt"
func main() {
is_divisible := func(x int, y int) bool{
var res bool
if x % y == 0 {
res = true
} else{
res = false
}
fmt.Println(res)
return res
}
fmt.Printf("%Tn", is_divisible)
is_divisible(10, 5)
is_divisible(33, 5)
divisblity_check := is_divisible(45, 5)
fmt.Printf("%T : %tn", divisblity_check, divisblity_check)
}
go run anonymous_function.go
func(int, int) bool
true
false
bool : true
Как мы видим, функция вернула булево значение и мы храним его в переменной divisblity_check
, затем можно вызвать переменную, содержащую функцию, т.е. is_divisible
, и таким образом мы получим возвращаемое значение в переменной как булево, поскольку мы выводим тип и значение переменной divisblity_check
. Мы также видим, что тип переменной is_divisible
имеет тип function literal, который принимает int, int
и имеет возвращаемое значение bool
.
Здесь мы можем сделать еще одну интересную вещь, которая была ограничена в вышеприведенных примерах. Мы можем напрямую вызвать функцию и сохранить ее как значение, а не саму функцию. Таким образом, мы можем использовать только значение, возвращенное из функции, но не можем вызвать функцию позже.
is_divisible := func(x int, y int) bool{
var res bool
if x % y == 0 {
res = true
} else{
res = false
}
fmt.Println(res)
return res
}(13, 4)
fmt.Printf("%Tn", is_divisible)
fmt.Printf(is_divisible)
go run anonymous_function.go
bool
false
Итак, в приведенном выше модифицированном примере мы передали параметр вместо вызываемой функции. Это позволит сохранить возвращаемое значение вызова функции. Таким образом, мы сохраним булево значение в is_divisible
и должны будем передать в функцию целочисленные значения, которые мы разобрали как (13, 4)
для вызова анонимной функции.
В приведенном ниже примере мы создали анонимную функцию, которая принимает три параметра (string, int, string)
и возвращает строку. Мы использовали функцию fmt.Sprintf
для форматирования переменной и сохранения ее в переменной, а затем возвращаем строку. Затем эта анонимная функция вызывается напрямую, и мы сохраняем возвращаемое значение вместо функции.
Таким образом, в этом примере переменная format_email
будет хранить строку, а не выступать в качестве функции как вызываемый объект.
package main
import "fmt"
func main() {
format_email := func(name string, age int, company string) string{
email := fmt.Sprintf("%s.%d@%s.com", name, age, company)
return email
}("john", 25, "gophersoft")
fmt.Println(format_email)
fmt.Printf("%Tn",format_email)
}
go run anonymous_function.go
john.25@gophersoft.com
string
Как мы видим, переменная format_email
возвращает только строку, а не вызываемый объект. Мы напрямую разобрали параметры анонимной функции, и поэтому она мгновенно вызывает ее, а мы возвращаем строку.
Передача анонимной функции в параметры функции
Мы можем даже передать анонимную функцию в качестве параметра функции. Это может быть очень полезно для написания простой логики внутри сложного сценария. Мы можем передать тип функции в качестве параметра, а затем разобрать фактические данные и выполнить нужную операцию.
Приведенный ниже пример представляет собой немного кода, но имеет большой смысл для понимания анонимных функций в golang. Функция get_csv
— это функция, которая принимает три параметра int, string, func(string)[] string
. Третий параметр — это литерал функции, который принимает строку и возвращает фрагмент строки. Таким образом, мы в основном преобразуем строку "kevin,21,john,33"
в фрагмент, например [[kevin 21] [john 33]]
, это в основном разделение значений с помощью ,
запятой в качестве разделителя, а затем обработка фрагментов для создания единого фрагмента.
package main
import "fmt"
import "strings"
func get_csv(index int, str string, t func(csv string)[]string) [][]string{
s := t(str)
var res [][]string
for i := 1; i<len(s); i+=2 {
var data []string
data = append(data, s[i-1], s[i])
res = append(res, data)
}
return res
}
func main() {
csv_slice := func (csv string) []string{
return strings.Split(csv, ",")
}
csv_data := get_csv(2,"kevin,21,john,33,george,24", csv_slice)
fmt.Println(csv_data)
for _, s := range csv_data{
fmt.Printf("%s - %sn",s[0],s[1])
}
go run functions/anonymous_function.go
[[kevin 21] [john 33] [george 24]]
kevin - 21
john - 33
george - 24
Давайте разберем код по порядку, начнем с главной функции, где у нас есть csv_slice
в качестве литерала функции и анонимной функции, которая принимает строку и возвращает фрагмент строки. Функция возвращает вызов функции strings.Split, принимающей строку из параметра функции. Затем мы вызываем функцию get_csv
с параметрами (2, "kevin,21....", csv_slice)
, эта функция определена вне main. Функция принимает три параметра, как обсуждалось и разбиралось в основной функции, и возвращает фрагмент типа string.
Итак, внутри функции get_csv
мы определяем s
как вызов функции t(str)
, которая, если вы внимательно посмотрите, является вызовом функции csv_slice
с параметром в виде строки. Этот вызов функции возвращает фрагмент строк, разделенных ,
. Вот и вся логика, необходимая для понимания анонимной функции от параметров. Мы использовали литерал функции для вызова функции из другой функции. В данном случае функция является анонимной функцией, назначенной переменной.
"kevin,21,john,33,george,24"
||
/
[kevin 21 john 33 george 24]
||
/
[[kevin 21] [john 33] [george 24]]
Далее, после того как у нас есть s
, который будет выглядеть как [kevin 21 john 33 george 24]
как каждый отдельный элемент. После этого мы создаем пустой срез строки среза как res
и работаем в цикле через срез, перескакивая по 2 индекса за раз. Почему? Потому что мы хотим получить срез двух элементов вместе. Поэтому мы также создаем фрагмент строки с именем data
и добавляем к нему два компонента как [kevin 21]
с помощью функции append, и этот фрагмент добавляется к фрагменту строки res
как [[kevin 21]]
, тем самым выполняя итерации по всему фрагменту и создавая желаемый фрагмент данных. Мы возвращаем res
из функции. Это возвращает нас к основной функции, которая просто итерирует срез и мы получаем из него нужные данные.
Вот так мы превращаем чрезвычайно простую задачу в чрезвычайно сложный код 🙂
На этом все. Ссылки на все примеры кода и команды можно найти в репозитории 100 days of Golang на GitHub.
Заключение
Вот и все, в этой части мы немного рассказали об анонимных функциях в golang. Анонимные функции — это просто литералы функций, которые могут быть использованы для выполнения множества быстрых операций без необходимости писать явную функцию в программе. Далее, в следующей части мы рассмотрим замыкания, которые немного сложны для понимания и требуют понимания анонимных функций.
Спасибо за прочтение, если у вас есть какие-либо пожелания, отзывы или вопросы, вы можете свободно задать их мне в моих социальных сетях. Счастливого кодинга 🙂