В компьютерном программировании чистая функция — это функция , обладающая следующими свойствами: [1] [2]
Некоторые авторы, особенно из сообщества императивных языков , используют термин «чистый» для всех функций, которые обладают указанным выше свойством 2 [3] [4] (обсуждается ниже).
Следующие примеры функций C++ являются чистыми:
floor
, возвращая пол числа;max
, возвращая максимум два значения.void f () { static std :: atomic < unsigned int > x = 0 ; ++ х ; }
x
можно наблюдать только внутри других вызовов f()
, и, поскольку оно f()
не передает значение x
своему окружению, оно неотличимо от функции void f() {}
, которая ничего не делает. Обратите внимание , x
что std::atomic
изменения в результате одновременного выполнения нескольких потоков f()
не приводят к гонке данных , которая имеет неопределенное поведение в C и C++.Следующие функции C++ являются нечистыми, поскольку им не хватает указанного выше свойства 1:
int f () { static int x = 0 ; ++ х ; вернуть х ; }
int f () { return x ; }
sin()
не является чистой, поскольку ее результат зависит от режима округления IEEE , который можно изменить во время выполнения.int f ( int * x ) { return * x ; }
int f () { int x знак равно 0 ; станд :: cin >> x ; вернуть х ; }
Следующие функции C++ являются нечистыми, поскольку им не хватает указанного выше свойства 2:
void f () { static int x = 0 ; ++ х ; }
пустота ж () { ++ х ; }
пустота ж ( int * x ) { +* x ; }
void f () { std :: cout << "Привет, мир!" << std :: endl ; }
Следующие функции C++ являются нечистыми, поскольку в них отсутствуют свойства 1 и 2:
int f () { static int x = 0 ; ++ х ; вернуть х ; }
int f () { int x знак равно 0 ; станд :: cin >> x ; вернуть х ; }
Ввод-вывод по своей сути нечист: операции ввода подрывают ссылочную прозрачность , а операции вывода создают побочные эффекты. Тем не менее, в некотором смысле функция может выполнять ввод или вывод и при этом оставаться чистой, если последовательность операций на соответствующих устройствах ввода-вывода моделируется явно как аргумент и результат, а операции ввода-вывода принимаются потерпеть неудачу, когда входная последовательность не описывает операции, фактически выполненные с момента начала выполнения программы. [ нужны разъяснения ]
Второй момент гарантирует, что единственная последовательность, которую можно использовать в качестве аргумента, должна меняться при каждом действии ввода-вывода; первый позволяет различным вызовам функции ввода-вывода возвращать разные результаты из-за изменения аргументов последовательности. [5] [6]
Монада ввода-вывода — это идиома программирования , обычно используемая для выполнения ввода-вывода на чисто функциональных языках.
Выходные данные чистой функции могут быть предварительно вычислены и кэшированы в справочной таблице. В методе, называемом мемоизацией , любой результат, возвращаемый данной функцией, кэшируется, и при следующем вызове функции с теми же входными параметрами кэшированный результат возвращается вместо повторного вычисления функции.
Мемоизацию можно выполнить, обернув функцию в другую функцию ( функцию-обертку ). [8]
С помощью мемоизации вычислительные усилия, необходимые для вычислений самой функции, могут быть уменьшены за счет накладных расходов на управление кэшем и увеличения требований к памяти.
Функции, которые обладают только указанным выше свойством 2, то есть не имеют побочных эффектов, допускают методы оптимизации компилятора, такие как исключение общих подвыражений и оптимизация цикла, аналогичная арифметическим операторам. [9] Примером C++ является length
метод, возвращающий размер строки, который зависит от содержимого памяти, на которое указывает строка, поэтому отсутствует указанное выше свойство 1. Тем не менее, в однопоточной среде следующий код C++
std :: string s = "Привет, мир!" ; int a [ 10 ] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 }; интервал л = 0 ; для ( int я знак равно 0 ; я < 10 ; ++ я ) { л += s . длина () + а [ я ]; }
можно оптимизировать так, чтобы значение s.length()
вычислялось только один раз перед циклом.
Некоторые языки программирования позволяют объявлять чистое свойство функции:
pure
ключевое слово можно использовать для объявления функции свободной от побочных эффектов (т.е. имеющей только указанное выше свойство 2). [10] Компилятор может вывести свойство 1 поверх объявления. [11]pure
определяет свойство 2, тогда как const
атрибут определяет действительно чистую функцию с обоими свойствами. [12]constexpr
C++ (оба свойства). [13]Поскольку чистые функции имеют одинаковые возвращаемые значения для одинаковых аргументов , они хорошо подходят для модульного тестирования .
Вот фундаментальные свойства чистой функции: 1. Функция возвращает один и тот же результат каждый раз, когда ее вызывают с одним и тем же набором аргументов. Другими словами, функция не имеет состояния и не может получить доступ к какому-либо внешнему состоянию. Каждый раз, когда вы его зовете, он ведет себя как новорожденный ребенок с пустой памятью и отсутствием знаний о внешнем мире. 2. Функция не имеет побочных эффектов. Вызов функции один раз — это то же самое, что вызов ее дважды и отбрасывание результата первого вызова.
Чистая функция — это функция, которая при одних и тех же входных данных всегда возвращает один и тот же результат и не имеет какого-либо наблюдаемого побочного эффекта.
assert()
прерывается с сообщением об ошибке, если его аргумент ложный; на 32-битной машине значения, выходящие за рамки fact(12)
, в любом случае не могут быть представлены.