В информатике операция, функция или выражение считаются имеющими побочный эффект , если они имеют какой-либо наблюдаемый эффект, отличный от их основного эффекта чтения значения своих аргументов и возврата значения вызывающему операцию. Примерами побочных эффектов являются изменение нелокальной переменной , статической локальной переменной или изменяемого аргумента, переданного по ссылке ; возникновение ошибок или исключений; выполнение ввода-вывода ; или вызов других функций с побочными эффектами. [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 ): возвращает - n если n < 0 иначе nутверждать абс ( абс ( - 3 )) == абс ( - 3 )
abs
является идемпотентным, поскольку второе применение abs
к возвращаемому значению первого применения к -3 возвращает то же значение, что и первое применение к -3.
Одной из распространенных демонстраций поведения побочных эффектов является оператор присваивания в C. Присваивание a = b
— это выражение, которое вычисляется с тем же значением, что и выражение b
, с побочным эффектом сохранения R-значения в b
L -значении . a
Это допускает множественное присваивание:
a = ( b = 3 ); // b = 3 вычисляется как 3, которое затем присваивается a
Поскольку оператор справа ассоциирует , это эквивалентно
а = б = 3 ;
Это может стать потенциальной проблемой для начинающих программистов, которые могут запутаться.
while ( b == 3 ) {} // проверяет, равно ли b 3
с
while ( b = 3 ) {} // b = 3 вычисляется как 3, что затем приводится к true, поэтому цикл бесконечен
Термин Side Effect относится к изменению нелокальной среды. Обычно это происходит, когда функция (или процедура) изменяет глобальную переменную или аргументы, переданные ссылочными параметрами. Но вот другие способы, которыми нелокальная среда может быть изменена. Мы рассматриваем следующие причины побочных эффектов через вызов функции: 1. Выполнение ввода-вывода. 2. Изменение глобальных переменных. 3. Изменение локальных постоянных переменных (например, статических переменных в C). 4. Изменение аргумента, переданного по ссылке. 5. Изменение локальной переменной, как автоматической, так и статической, функции, расположенной выше в последовательности вызова функции (обычно через указатель).