Что такое «побочный эффект»?


89

Я не совсем понял концепцию побочного эффекта.

  • Что такое побочный эффект в программировании?
  • Это зависит от языка программирования?
  • Есть ли такая вещь, как внешние и внутренние побочные эффекты?

Пожалуйста, приведите пример причин, которые создают побочные эффекты.


7
Звучит очень похоже на домашнюю работу.
gnasher729

3
@ gnasher729, кого это волнует, НАСТОЯТЕЛЬНО полезно :)
Чарли Паркер

Ответы:


109

Побочный эффект относится просто к изменению некоторого состояния - например:

  • Изменение значения переменной;
  • Запись некоторых данных на диск;
  • Включение или отключение кнопки в пользовательском интерфейсе.

Вопреки тому, что некоторые люди, кажется, говорят:

  • Побочный эффект не должен быть скрытым или неожиданным (это может быть, но это не имеет ничего общего с определением, которое применяется к информатике);

  • Побочный эффект не имеет ничего общего с идемпотентностью. Идемпотентная функция может иметь побочные эффекты, а неидемпотентная функция может не иметь побочных эффектов (например, получение текущей системной даты и времени).

Это действительно очень просто. Побочный эффект = что-то меняется.

PS Как указывает комментатор Бенджол, несколько человек могут смешивать определение побочного эффекта с определением чистой функции , которая является (а) идемпотентной и (б) не имеет побочных эффектов. Один не подразумевает другого в общей компьютерной науке, но функциональные языки программирования, как правило, имеют тенденцию усиливать оба ограничения.


38
Фраза «побочный эффект» звучит так, как будто меняется что - то еще , кроме того, что предполагалось. В медицине лекарство будет иметь основной эффект уменьшения боли, а иногда побочный эффект, вызывающий кровотечение из носа, головокружение и т. Д. ... цель препарата не в том, чтобы вызвать кровотечение из носа, но иногда это происходит как непреднамеренный дополнительный результат.
FrustratedWithFormsDesigner

15
@ Разочарованный: +1. Всякий раз, когда я вижу этот термин, я не могу не задаться вопросом, не был ли он выбран адвокатами ФП для создания именно этой тонко зловещей коннотации.
Мейсон Уилер

6
@ Мейсон Уилер. Это существовало задолго до ФП. И это не тонкий зловещий оттенок. Это полное зло и всегда было. В течение трех десятилетий, которые я кодировал, утверждение «крипто-назначения» - побочный эффект - беспокоило людей. С простым старым оператором присваивания гораздо легче справиться.
С.Лотт

7
@ Мейсон Уилер: В кн ++a. Не похоже на назначение. b = ++a;имеет два побочных эффекта. Очевидное и крипто-назначение a. Это та вещь, которая является побочным эффектом, который (для некоторых) желателен. Но за всю мою карьеру это назвали побочным эффектом, чтобы сделать его не тонким.
С.Лотт

5
@ Захари, пожалуйста, посмотрите последний пункт в моем ответе. То, что вы имеете в виду, является идемпотентным поведением (или его отсутствием). Это ничего не говорит вам о побочных эффектах. Проверка системных часов не является побочным эффектом; на самом деле, любая функция или метод с префиксом «get» - это та, которую вы должны разумно ожидать, чтобы не иметь никаких побочных эффектов.
Aaronaught

36

Говорят, что любая операция, которая изменяет состояние компьютера или взаимодействует с внешним миром, имеет побочный эффект. Смотрите Википедию о побочных эффектах .

Например, эта функция не имеет побочных эффектов. Его результат зависит только от входных аргументов, и при вызове ничего не меняется в состоянии программы или ее окружения:

int square(int x) { return x * x; }

Напротив, вызов этих функций даст вам разные результаты в зависимости от порядка их вызова, потому что они что-то меняют в состоянии компьютера:

int n = 0;
int next_n() { return n++; }
void set_n(int newN) { n = newN; }      

Эта функция имеет побочный эффект записи данных для вывода. Вы не вызываете функцию, потому что хотите получить возвращаемое значение; Вы называете это потому, что хотите, чтобы эффект, который он оказывает на «внешний мир»:

int Write(const char* s) { return printf("Output: %s\n", s); }

1
Это хорошее определение, но я не в восторге от проработки - так же, как и в ответе Торбьерна, часть его, кажется, связывает проблему побочных эффектов с проблемой идемпотентных функций; как Writeпоказывает ваш пример, наличие побочных эффектов не означает, что функция когда-либо меняет свой вывод относительно своих входов, или даже что ее выход вообще зависит от входа.
Aaronaught

6
Дело не в идемпотенте. Факт, что это производит продукцию, означает, что у нее есть побочный эффект.
Кристофер Джонсон

В некоторых системах вызов square(x)может привести к загрузке модуля, в котором определена функция, с диска. Должно ли это считаться побочным эффектом? В конце концов, это тот факт, что (первый) вызов занимает неожиданно много времени,
Хаген фон

1
@HagenvonEitzen Каждая операция на самом деле вносит изменения в состояние компьютера (регистры процессора, память, энергопотребление, нагрев и т. Д.). «Побочный эффект» обычно относится к воображаемой идеализированной среде выполнения, где ничего в этой среде не изменяется, если только программа явно не изменяет ее. Но если вы звоните, square(x) потому что хотите, чтобы состояние внешнего компьютера изменилось, вы можете считать это побочным эффектом.
Кристофер Джонсон

Для меня первая иллюстрация имеет смысл. Тем не менее, второй менее. Я считаю, что побочный эффект должен быть определен по отношению к определенной среде / сфере применения. Если вы рассматриваете всю вселенную, нет такого понятия, как побочный эффект. Даже если вы ограничиваете его компьютером, ваша функция будет влиять на другие процессы, так как процессор не будет вести себя так же. Если вы ограничите область действия вещами, доступными в локальной области функций, то нам есть о чем поговорить.
funct7

21

Я думаю, что существующие ответы довольно хороши. Я хотел бы остановиться на некоторых аспектах, которые IMO недостаточно подчеркнули.

В математике функция - это просто отображение кортежа значений в значение. Так что, учитывая функцию fи значение x, f(x)всегда будет один и тот же результат y. Вы вполне можете заменить f(x)на yвезде в выражении, и ничего не изменится.

То, что во многих языках программирования называется функцией (или процедурой), является конструкцией (частью кода), которая может быть выполнена, потому что:

  1. Он вычисляет функцию в математическом смысле, то есть, учитывая входные значения, возвращает результат или
  2. Это производит некоторый эффект, например, печатает что-то на экране, изменяет значение в базе данных, запускает ракеты, спит в течение 10 секунд, отправляет SMS.

Таким образом, эффекты могут быть связаны с состоянием, но также и с другими аспектами, такими как запуск ракеты или приостановка выполнения на несколько секунд.

Термин «побочный эффект» может звучать отрицательно, но обычно эффект вызова функции является самой целью самой функции. Я предполагаю, что, поскольку термин «функция» изначально использовался в математике, вычисление значения считается основным эффектом функции, тогда как любые другие эффекты считаются побочными эффектами . Некоторые языки программирования используют термин процедура, чтобы избежать путаницы с функциями в математическом смысле.

Обратите внимание, что

  1. Некоторые процедуры полезны как для их возвращаемого значения, так и для их побочного эффекта.
  2. Некоторые процедуры только вычисляют значение результата и не имеют других эффектов. Их часто называют чистыми функциями, потому что все, что они делают, это вычисляют функцию в математическом смысле.
  3. Некоторые процедуры, например, sleep()в Python, полезны только для их (побочных) эффектов. Они часто моделируются как функции, которые возвращают специальное значение None, или unitили ()или ..., которое просто указывает, что вычисление завершено правильно.

2
По моему скромному мнению, это должен быть принятый ответ. Понятие побочного эффекта имеет смысл даже с точки зрения математических функций. Процедура разработана, чтобы просто структурировать набор инструкций, позволяя вам переходить к этому набору из любого места и обратно. Там нет основного предполагаемого эффекта и побочных. Возможно, вы могли бы сказать, что выбрасывание исключения является побочным эффектом процедуры, так как оно нарушает цель процедуры, которая должна вернуть вас туда, где вы остановились, и продолжить там выполнение формы.
Дидье А.

4

Побочный эффект - это когда операция оказывает влияние на переменную / объект, который находится за пределами предполагаемого использования.

Это может произойти, когда вы вызываете сложную функцию, побочным эффектом которой является изменение некоторой глобальной переменной, даже если вы не вызвали ее (возможно, вы вызвали ее для извлечения чего-либо из базы данных).

Я признаю, что мне трудно придумать простой пример, который не выглядит полностью надуманным, а примеры из того, над чем я работал, слишком длинны, чтобы их можно было здесь разместить (и, поскольку это связано с работой, мне, вероятно, не следует в любом случае ).

Одним примером, который я видел (некоторое время назад), была функция, которая открывала соединение с базой данных, если соединение было в закрытом состоянии. Проблема заключалась в том, что предполагалось закрыть соединение в конце функции, но разработчик забыл добавить этот код. Итак, здесь был непреднамеренный побочный эффект: при вызове процедуры должен был выполняться только запрос, и побочным эффектом было то, что соединение оставалось открытым, и если функция вызывалась дважды подряд, возникла бы ошибка, сообщающая, что соединение было уже открыт.


Хорошо, так как все сейчас приводят примеры, я думаю, что я тоже буду;)

/*code is PL/SQL-styled pseudo-code because that's what's on my mind right now*/

g_some_global int := 0; --define a globally accessible variable somewhere.

function do_task_x(in_a in number) is
begin
    b := calculate_magic(in_a);
    if b mod 2 == 0 then
        g_some_global := g_some_global + b;
    end if;
    return (b * 2.3);
end;

Функция do_task_xимеет основной эффект возврата результата некоторых вычислений и побочный эффект возможного изменения глобальной переменной.

Разумеется, что является основным, а побочный эффект может быть открытым для интерпретации и может зависеть от фактического использования. Если я вызываю эту функцию с целью изменения глобального и отбрасываю возвращаемое значение, то я бы сказал, что изменение глобального является основным эффектом.


2
Я не думаю, что это хорошее универсальное определение. Многие программисты намеренно используют конструкции специально для побочного эффекта.
CB Bailey

@ Чарльз: Достаточно справедливо. В таком случае, как бы вы это определили?
FrustratedWithFormsDesigner

2
Я думаю, что @KristopherJohnson имеет самое четкое определение. Все, что изменяет это состояние программы или ее среды или производит эффект реального мира, такой как генерирование результатов.
CB Bailey

@Charles Bailey: Это не меняет определения. Использование вещей для побочного эффекта в порядке. Пока вы понимаете, что есть побочный эффект. Это ничего не меняет в этом определении.
С.Лотт

1
@SLott: определение в этом ответе (то есть в первом абзаце) включает в себя предложение: «за пределами предполагаемого использования». Я думаю, что мой комментарий был справедливым.
CB Bailey

3

В информатике функция или выражение, как говорят, имеют побочный эффект, если они изменяют некоторое состояние или имеют наблюдаемое взаимодействие с вызывающими функциями или внешним миром.

Из Википедии - Побочный эффект

Функция, в математическом смысле, является отображением ввода-вывода. Предполагаемый эффект вызова функции - сопоставить входные данные с выходными данными, которые она возвращает. Если функция делает что-то еще, не имеет значения, что, но если у нее есть какое-либо поведение, которое не отображает входные данные на выход, это поведение, как известно, является побочным эффектом.

В более общем смысле, побочным эффектом является любой эффект, который не является предполагаемым эффектом дизайнера конструкции.

Эффект - это все, что влияет на актера. Если я вызываю функцию, которая отправляет моей девушке текстовое сообщение о разрыве, которое затрагивает группу актеров, меня, ее, сеть компании сотовой связи и т. Д. Единственный предполагаемый эффект вызова функции, свободной от побочных эффектов, - это функция вернуть мне отображение из моего ввода. Таким образом, для:

   public void SendBreakupTextMessage() {
        Messaging.send("I'm breaking up with you!")
   }

Если это предназначено для функции, то единственное, что нужно сделать, это вернуть void. Если он не имеет побочных эффектов, он не должен отправлять текстовое сообщение.

В большинстве языков программирования нет конструкции для математической функции. Ни одна конструкция не предназначена для использования в качестве таковой. Вот почему большинство языков говорят, что у вас есть методы или процедуры. По замыслу, они предназначены для того, чтобы иметь возможность делать гораздо больше эффектов. На обычном языке программирования никто не заботится о том, каким был метод или процедура, поэтому, когда кто-то говорит, что эта функция имеет побочный эффект, они фактически подразумевают, что эта конструкция не ведет себя как математическая функция. И когда кто-то говорит, что эта функция не имеет побочных эффектов, они подразумевают, что эта конструкция эффективно ведет себя как математическая функция.

Чистая функция всегда свободна от побочных эффектов по определению. Чистая функция, это способ сказать, что эта функция, даже несмотря на то, что она использует конструкцию, которая допускает большее количество эффектов, имеет эффект, равный эффекту математической функции.

Я призываю любого сказать мне, когда функция без побочных эффектов не будет чистой. Если основной предполагаемый эффект контекста предложения, использующего термин чистый и свободный побочный эффект, не является эффектом математического предполагаемого эффекта функции, то они всегда равны.

Таким образом, иногда, хотя и реже, и я полагаю, что это различие отсутствует, а также вводит в заблуждение людей (поскольку это не самое распространенное предположение) в принятом ответе, но иногда предполагается, что предполагаемый эффект функции программирования сопоставить ввод с выводом, где ввод не ограничен явными параметрами функции, но вывод ограничен явным возвращаемым значением. Если вы предполагаете, что это предполагаемый эффект, то функция, считывающая файл и возвращающая другой результат в зависимости от того, что находится в файле, по-прежнему не имеет побочных эффектов, так как вы позволили входным данным поступать из других мест в вашем предполагаемом эффекте.

Итак, почему это все важно?

Это все о контроле и сохранении. Если вы вызываете функцию, и она делает что-то еще, а затем возвращает значение, трудно понять ее поведение. Вам нужно будет заглянуть внутрь функции для реального кода, чтобы угадать, что он делает, и подтвердить его правильность. Идеальная ситуация заключается в том, что очень ясно и легко узнать, какой ввод использует функция, и что она больше ничего не делает, затем возвращает вывод для нее. Вы можете немного расслабиться и сказать, что точно знать, какой ввод он использует, не так полезно, как быть уверенным, что он не делает ничего другого, о чем вы можете не знать, а затем вернуть значение, так что, возможно, вы удовлетворены только принудительным применением что он не делает ничего другого, кроме отображения входных данных, независимо от того, откуда он их получает, для вывода.

Практически во всех случаях смысл программы состоит в том, чтобы иметь эффекты, отличные от сопоставления вещей, входящих в исходящие вещи. Идея управления побочным эффектом заключается в том, что вы можете организовать код так, чтобы его было легче понять и обдумать. Если вы соберете все побочные эффекты вместе, в месте, которое является очень явным и центральным, легко узнать, где искать и верить, что это все, что происходит, не более. Если у вас также очень явный ввод, это помогает проверить поведение для другого ввода, и его проще использовать, так как вам не нужно менять ввод во многих разных местах, некоторые из которых могут быть неочевидными, просто чтобы получить то, что вы хотите.

Поскольку наиболее полезным для понимания, обоснования и управления поведением программы является то, чтобы все входные данные были четко сгруппированы вместе и явными, а также чтобы все побочные эффекты были сгруппированы вместе и явными, об этом обычно говорят люди, когда говорят побочный эффект, чистый и т. д.

Поскольку наиболее полезным является группировка побочных эффектов и их явность, иногда люди будут только подразумевать это и различать это, говоря, что это не чисто, но все же «побочный эффект» свободен. Но побочный эффект относится к предполагаемому «предполагаемому первичному эффекту», поэтому это контекстуальный термин. Я считаю, что это используется реже, хотя на удивление много говорится в этой теме.

Наконец, идемпотент означает, что вызов этой функции много раз с одними и теми же входами (независимо от того, откуда они берутся) всегда будет приводить к одинаковым эффектам (побочный эффект или нет).


Я думаю, что большая проблема с объяснением побочных эффектов заключается в том, что до тех пор, пока вы не используете язык, такой как Ocaml или Haskell, будет очень трудно рассуждать о программировании без побочных эффектов (почти!).
Джейми Стросс

2

При программировании побочный эффект - это когда процедура изменяет переменную вне ее области видимости. Побочные эффекты не зависят от языка. Есть несколько классов языков, которые нацелены на устранение побочных эффектов (чисто функциональные языки), но я не уверен, есть ли такие, которые требуют побочных эффектов, но я могу ошибаться.

Насколько я знаю, внутренних и внешних побочных эффектов нет.


Точнее говоря, чисто функциональные языки четко отделяют свободный от побочных эффектов код от другого кода, тогда как в других языках нет механизма, позволяющего различать чистый и нечистый код. Большинство программ должны иметь побочные эффекты, чтобы иметь какое-либо применение.
Джорджио

Я думаю, что некоторые языки программирования pre-gui, такие как MS-BASIC и QBasic, возможно, были настолько близки к языку «только побочный эффект», насколько вы можете себе это позволить. И да, вы можете иметь как внутренние, так и внешние побочные эффекты.
Джеймс К

0

Вот простой пример:

int _totalWrites;
void Write(string message)
{
    // Invoking this function has the side effect of 
    // incrementing the value of _totalWrites.
    _totalWrites++;
    Debug.Write(message);
}

Определение побочного эффекта не является специфическим для программирования, поэтому просто представьте себе побочные эффекты ваших лекарств или употребление слишком большого количества пищи.


Но если сообщение приходит как ссылка и вы меняете сообщение в своем методе, это может быть побочным эффектом. Я прав?
Амир Резаи

Тот факт, что выражение x++изменяет переменную x, обычно считается побочным эффектом. Это значение выражения является значением перед приращением x; это часть побочного эффекта выражения.
CB Bailey

@ Чарльз - я согласен, хотя оригинальный пример был не так ясен, как текущий.
ChaosPandion

@ Амир - Ну, это действительно зависит от языка. Если бы это был C #, это не считалось бы побочным эффектом.
ChaosPandion

@ChaosPandion: Лично я не согласен. Первоначальный пример был намного проще и понятнее.
CB Bailey

-2

Побочным эффектом являются вещи, которые происходят в коде, которые не являются очевидными.

Например, допустим, у вас есть этот класс

public class ContrivedRandomGenerator {
   public int Seed { get; set; }

   public int GetRandomValue()
   {
      Random(Seed);
      Seed++;
   }
}

Когда вы изначально создаете класс, вы даете ему семя.

var randomGenerator = new ContrivedRandomGenerator();
randomGenerator.Seed = 15;
randomGenerator.GetRandomValue();

Вы не знаете внутренности, вы просто ожидаете получить случайное значение, и вы ожидаете, что randomGenerator.Seed все равно будет 15 ... но это не так.

У вызова функции был побочный эффект изменения значения Seed.


10
Побочные эффекты не должны быть скрыты. Вы думаете о разговорном или медицинском использовании; в программировании побочный эффект просто относится к изменению некоторого состояния.
Aaronaught

1
Печать на консоль является побочным эффектом. Это не скрыто. Из Википедии : «В информатике функция или выражение, как говорят, имеют побочный эффект, если, помимо возврата значения, они также изменяют некоторое состояние или имеют наблюдаемое взаимодействие с вызывающими функциями или внешним миром ».

Побочные эффекты - это то, как не-функции (т.е. процедуры) выполняют любую работу. Х = 1; X = Y (10) две чистые функции. Когда вы выходите за пределы области «x = what», записывать ли вывод на экран | drive | printer | led или читать ввод вне формата «x = y» или просто менять значение переменной с одной вещи на другую Это побочный эффект.
Джеймс К

Я думаю, что под «скрытым» он подразумевает неочевидное. Как и в x = f (y, z), можно предположить, что x основан на y и z. Принимая во внимание, что proc (x, y, z) ничего не говорит вам о том, что происходит. Каждый varable может быть изменен, или нет. Proc может быть аналогом f или совершенно не связанным. Чистая функция имеет один ответ: «х». Пройдите дальше, это побочный эффект. Целенаправленно, но побочные эффекты.
Джеймс К

Точно так же, как для понимания 0, вы должны сначала понять 1: чтобы понять побочные эффекты, вы должны сначала понять функции.
Джеймс К
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.