В информатике говорят , что операция, функция или выражение имеют побочный эффект, если они изменяют некоторые значения переменных состояния за пределами своей локальной среды, то есть имеют ли они какой-либо наблюдаемый эффект, отличный от основного эффекта возврата значение для инициатора операции. Примеры побочных эффектов включают изменение нелокальной переменной , изменение статической локальной переменной , изменение изменяемого аргумента, передаваемого по ссылке , выполнение ввода-вывода или вызов других функций с побочными эффектами. [1] При наличии побочных эффектов поведение программы может зависеть от истории; то есть порядок оценки имеет значение. Понимание и отладка функции с побочными эффектами требует знаний о контексте и ее возможной истории. [2] [3]
Побочные эффекты играют важную роль при разработке и анализе языков программирования . Степень использования побочных эффектов зависит от парадигмы программирования. Например, императивное программирование обычно используется для создания побочных эффектов, для обновления состояния системы. Напротив, декларативное программирование обычно используется для сообщения о состоянии системы без побочных эффектов.
Функциональное программирование направлено на минимизацию или устранение побочных эффектов. Отсутствие побочных эффектов облегчает формальную проверку программы. Функциональный язык Haskell устраняет побочные эффекты, такие как ввод-вывод и другие вычисления с сохранением состояния, заменяя их монадическими действиями. [4] [5] Функциональные языки, такие как Standard ML , Scheme и Scala , не ограничивают побочные эффекты, но программисты обычно избегают их. [6]
Программисты на языке ассемблера должны знать о скрытых побочных эффектах — инструкциях, которые изменяют части состояния процессора, не упомянутые в мнемонике инструкции. Классическим примером скрытого побочного эффекта является арифметическая инструкция, которая неявно изменяет коды условий (скрытый побочный эффект), одновременно изменяя регистр ( предполагаемый эффект). Одним из потенциальных недостатков набора команд со скрытыми побочными эффектами является то, что если многие инструкции имеют побочные эффекты на одну часть состояния, например коды условий, то логика, необходимая для последовательного обновления этого состояния, может стать узким местом в производительности. Проблема особенно остра на некоторых процессорах, разработанных с конвейерной обработкой (с 1990 года) или с внеочередным выполнением . Такому процессору может потребоваться дополнительная схема управления для обнаружения скрытых побочных эффектов и остановки конвейера, если следующая инструкция зависит от результатов этих эффектов.
Отсутствие побочных эффектов является необходимым, но недостаточным условием ссылочной прозрачности. Ссылочная прозрачность означает, что выражение (например, вызов функции) можно заменить его значением. Для этого требуется, чтобы выражение было чистым , то есть выражение должно быть детерминированным (всегда давать одно и то же значение для одних и тех же входных данных) и не иметь побочных эффектов.
Побочные эффекты, вызванные временем, затрачиваемым на выполнение операции, обычно игнорируются при обсуждении побочных эффектов и ссылочной прозрачности. В некоторых случаях, например, при синхронизации оборудования или тестировании, операции вставляются специально из-за их временных побочных эффектов, например sleep(5000)
или for (int i = 0; i < 10000; ++i) {}
. Эти инструкции не меняют состояние, за исключением того, что их выполнение занимает некоторое время.
Подпрограмма с побочными эффектами является идемпотентной, если несколько применений подпрограммы оказывают такое же влияние на состояние системы, как и одно применение, другими словами, если функция из пространства состояний системы в себя, связанная с подпрограммой, идемпотентна в математическом смысле . Например, рассмотрим следующую программу Python :
х = 0def setx ( n ): глобальный x x = nsetx ( 3 ) утверждать x == 3 setx ( 3 ) утверждать x == 3
setx
является идемпотентным, поскольку второе применение setx
значения 3 оказывает тот же эффект на состояние системы, что и первое применение: x
уже было установлено значение 3 после первого применения, и оно все еще установлено на 3 после второго применения.
Чистая функция идемпотентна, если она идемпотентна в математическом смысле . Например, рассмотрим следующую программу Python:
def abs ( n ): return - n, если n < 0 , иначе nутверждать абс ( абс ( - 3 )) == абс ( - 3 )
abs
является идемпотентным, поскольку второе применение abs
к возвращаемому значению первого приложения к -3 возвращает то же значение, что и первое приложение к -3.
Одной из распространенных демонстраций поведения побочных эффектов является оператор присваивания в C. Присваивание a = b
— это выражение, результатом которого является то же значение, что и выражение b
, с побочным эффектом сохранения R-значения в b
L - значении a
. Это позволяет множественное назначение:
а = ( б = 3 ); // b = 3 оценивается как 3, которое затем присваивается
Поскольку право оператора связывает , это эквивалентно
а = б = 3 ;
Это представляет собой потенциальное зависание для начинающих программистов, которые могут запутать
while ( b == 3 ) {} // проверяет, имеет ли значение b значение 3
с
while ( b = 3 ) {} // b = 3 оценивается как 3, что затем приводит к true, поэтому цикл бесконечен
Термин «побочный эффект» относится к модификации нелокальной среды. Обычно это происходит, когда функция (или процедура) изменяет глобальную переменную или аргументы, передаваемые ссылочными параметрами. Но есть и другие способы изменения нелокальной среды. Мы рассматриваем следующие причины побочных эффектов при вызове функции: 1. Выполнение ввода-вывода. 2. Изменение глобальных переменных. 3. Изменение локальных постоянных переменных (например, статических переменных в C). 4. Изменение аргумента, передаваемого по ссылке. 5. Изменение локальной переменной, автоматической или статической, функции, расположенной выше в последовательности вызова функции (обычно с помощью указателя).