На этом шаге рассмотрим рекурсивные функции в Go.
Поскольку в языке Go функции являются обычными значениями, ссылки на них можно сохранять в переменных, что делает возможным выбирать, какую функцию вызвать, во время выполнения. Кроме того, возможность создавать замыкания фактически позволяет создавать функции во время выполнения, то есть можно иметь две или более различных реализаций одной и той же функции (использующих разные алгоритмы), и создавать только одну из них для дальнейшего использования.
Другой сценарий, связанный с выбором функции во время выполнения, – когда имеется две или более функций с одинаковой функциональностью, но реализующих разные алгоритмы и было бы нежелательно производить выбор функции на этапе компиляции (например, чтобы дать пользователю возможность самому выбирать ту или иную реализацию с целью тестирования).
Например, для работы со строками, состоящими только из 7-битных ASCII-символов, можно написать функцию IsPalindrome(), и во время выполнения создавать ту версию функции, которая действительно необходима.
Реализовать это можно, объявив в пакете переменную с типом, совпадающим с сигнатурой функции, и затем создать соответствующую функцию в функции init().
var IsPalindrome func(string) bool // Для хранения ссылки на функцию func init() { if len(os.Args) > 1 &&(os.Args[1] == "-a" || os.Args[1] == "--ascii") { os.Args = append(os.Args[:1], os.Args[2:]...) // Отбросить аргумент IsPalindrome = func(s string) bool { // Простейшая ASCII-версия if len(s) <= 1 { return true } if s[0] != s[len(s)-1] { return false } return IsPalindrome(s[1 : len(s)-1]) } } else { IsPalindrome = func(s string) bool { // Версия для UTF-8 // ... } } }
Выбор той или иной реализации функции IsPalindrome() происходит на основе аргумента командной строки. Если аргумент указан, он удаляется из среза os.Args (благодаря чему остальная програм ма даже знать не будет о его существовании), и создается версия функции IsPalindrome() для работы со строками, состоящими из 7-битных ASCII-символов. Удаление аргумента из среза os.Args выглядит немного замысловато, потому что требуется сохранить в нем первый, третий и все последующие аргументы, кроме второго ("-a" или "--ascii"). Здесь нельзя использовать выражение os.Args[0] в вызове функции append(), потому что первым аргументом этой функции должен быть срез, поэтому здесь используется выражение os.Args[:1], возвращающее срез с единственным элементом os.Args[0]. Если флаг выбора ASCII-режима отсутствует, создается представленная ранее версия, которая корректно работает и с 7-битными ASCII-строками, и со строками Юникода в кодировке UTF-8. В остальной части программы функция IsPalindrome() может вызываться как обычно, хотя фактическая ее реализация будет отличаться в зависимости от выбранной версии.
На следующем шаге рассмотрим ключевые понятия ООП в Go.