Що таке "побічний ефект"?


87

Я чітко не зрозумів поняття побічної дії.

  • Що таке побічний ефект у програмуванні?
  • Чи залежить від мови програмування?
  • Чи існує таке поняття, як зовнішні та внутрішні побічні ефекти?

Наведіть приклад причин, які створюють побічні ефекти.


7
Звучить дуже як домашнє завдання.
gnasher729

3
@ gnasher729, кого це хвилює, ТРЕМЕНДНО корисно :)
Чарлі Паркер

Відповіді:


108

Побічним ефектом відноситься просто до модифікації якого - то держави - наприклад:

  • Зміна значення змінної;
  • Запис деяких даних на диск;
  • Увімкнення або вимкнення кнопки в інтерфейсі користувача.

Всупереч тому, що деякі люди, здається, говорять:

  • Побічний ефект не повинен бути прихованим чи несподіваним (це може бути, але це не має нічого спільного з визначенням, оскільки це стосується інформатики);

  • Побічний ефект не має нічого спільного з ідемпотенцією. Ідентифікуюча функція може мати побічні ефекти, а неімпотентна функція може не мати побічних ефектів (наприклад, отримання поточної дати та часу системи).

Це дійсно дуже просто. Побічний ефект = десь щось змінюється.

PS Як зазначає коментатор benjol, кілька людей можуть суперечити визначення побічного ефекту з визначенням чистої функції , що є функцією, яка (а) є безсильною та (b) не має побічних ефектів. Одне не передбачає іншого в загальній інформатиці, але функціональні мови програмування, як правило, мають право виконувати обидва обмеження.


38
Словосполучення «побічний ефект» дає змогу звучати як щось інше , ніж те, що було призначено. У медицині лікарський засіб матиме головний ефект зменшення болю, а іноді і побічного ефекту від спричинення кровотеч з носа, запаморочення тощо ... мета препарату - не викликати кровотечі з носа, але іноді це відбувається як ненавмисний додатковий результат.
FrustratedWithFormsDesigner

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

6
@ Мейсон Уілер. Він існував задовго до ФП. І це не тонко зловісна конотація. Це було відверто зло і завжди було. Протягом 3-х десятиліть, які я кодував, заява про "присвоєння криптовалют" - побічний ефект - хвилює людей. З простою старою заявою про призначення впоратися набагато простіше.
S.Lott

7
@Mason Уілер: Пн ++a. Не схоже на призначення. b = ++a;має два побічні ефекти. Очевидна і криптовалюта a. Це та річ, яка є побічним ефектом, який (для деяких) бажаний. Але мене називали побічним ефектом протягом усієї моєї кар’єри, щоб зробити його не витонченим.
С.Лотт

5
@Zachary, будь ласка, дивіться останню точку кулі у моїй відповіді. Те, про що ви маєте на увазі, - це безсильна поведінка (або її відсутність). Це нічого не говорить про побічні ефекти. Перевірка системного годинника не є побічним ефектом; насправді, будь-яка функція чи метод із префіксом слова "отримати" - це той варіант, від якого слід обґрунтовано очікувати, що він не матиме побічних ефектів.
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

20

Я вважаю, що наявні відповіді досить хороші. Я хотів би детальніше зупинитися на деяких аспектах, на які ІМО недостатньо підкреслив.

У математиці функція - це просто відображення від набору значень до значення. Таким чином, з огляду на функції 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

@Charles: Досить справедливо. У такому випадку, як би ви це визначили?
FrustratedWithFormsDesigner

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

@Charles Bailey: Це не змінює визначення. Використовувати речі для побічного ефекту - прекрасно. Поки ви розумієте, що є побічний ефект. Це нічого не змінює щодо цього визначення.
С.Лотт

1
@SLott: Визначення у цій відповіді (тобто першому абзаці) включає в себе пункт: "поза межами призначеного використання". Я думаю, що мій коментар був справедливим.
CB Bailey

3

У інформатиці функція або вираз мають побічний ефект, якщо вони змінюють певний стан або спостерігають взаємодію з функціями виклику або зовнішнім світом.

З Вікіпедії - побічний ефект

Функція, в математичному сенсі, - це відображення від введення до виводу. Запропонований ефект виклику функції полягає в тому, щоб вона відображала вхід на вихід, який він повертає. Якщо функція робить щось інше, не має значення, але якщо вона має будь-яку поведінку, яка не відображає вхід на вихід, така поведінка, як відомо, є побічним ефектом.

У більш загальному розумінні побічним ефектом є будь-який ефект, який не є передбачуваним ефектом проектувальника конструкції.

Ефект - це все, що впливає на актора. Якщо я називаю функцію, яка надсилає моїй дівчині розривне текстове повідомлення, яке впливає на купу акторів, мене, її, мережу компанії мобільного телефону тощо. щоб повернути мені карту зі свого введення. Отже для:

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

Якщо це призначено для функції, то єдине, що він повинен зробити, це повернути недійсність. Якщо це було побічним ефектом, воно фактично не повинно надсилати текстове повідомлення.

У більшості мов програмування немає побудови для математичної функції. Жодна конструкція не передбачається використовувати як таку. Ось чому більшість мов кажуть, що у вас є методи чи процедури. За задумом вони призначені для досягнення багатьох інших ефектів. У загальній мові програмування ніхто насправді не переймається наміром того, яким був метод чи процедура, тому коли хтось каже, що ця функція має побічну дію, вони фактично означають, що ця конструкція не поводиться як математична функція. І коли хтось каже, що ця функція не має побічних ефектів, це означає, що ця конструкція ефективно поводиться як математична функція.

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

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

Як таке, іноді, хоч і рідше, і я вважаю, що цього розходження не вистачає, а також оманливих людей (як це не найпоширеніше припущення) у прийнятій відповіді, але іноді передбачається, що наміченим ефектом функції програмування є для відображення вводу на вихід, де введення не обмежується явними параметрами функції, але вихід обмежується явним значенням повернення. Якщо ви припускаєте, що це призначений ефект, то функція зчитування файлу та повернення іншого результату на основі того, що є у файлі, все ще є вільним від побічних ефектів, оскільки ви дозволяли вводити дані з інших місць у вашому наміченому ефекті.

Отже, чому це все важливо?

Вся справа в контролі та утриманні. Якщо ви викликаєте функцію, і вона робить щось інше, то повертає значення, важко міркувати про її поведінку. Вам потрібно буде заглянути всередину функції для фактичного коду, щоб відгадати, що він робить, і підтвердити його правильність. Ідеальна ситуація полягає в тому, що дуже зрозуміло і легко дізнатися, який саме вхід використовує функція, і що він нічого не робить, то повертаючи вихід для цього. Ви можете трохи розслабитися і сказати, що точно знати, який саме вхід використовується, - це не так корисно, як впевненість, що ви не робите нічого іншого, чого, можливо, не знаєте, потім повертаєте значення, тому, можливо, вас влаштовує лише примусове виконання що він не робить нічого іншого, ніж введення карти, незалежно від того, звідки він отримує, для виведення.

Майже у всіх випадках сенс програми полягає в тому, щоб мати інші ефекти, а не відображення речей, що надходять до речей, що виходять. Ідея контролю побічного ефекту полягає в тому, що ви можете організувати код таким чином, щоб його було легше зрозуміти і міркувати. Якщо ви покладете всі побічні ефекти разом, в дуже чітке і центральне місце, легко дізнатися, де шукати і вірити, що це все, що відбувається, не більше. Якщо ви також маєте дуже чітке введення, це допомагає перевірити поведінку для різних вхідних даних, і це простіше у використанні, оскільки вам не потрібно змінювати вхід у багатьох різних місцях, деякі з яких можуть бути не очевидними, просто щоб отримати те, що ти хочеш.

Оскільки найбільш корисним для розуміння, обґрунтування та контролю поведінки програми є те, щоб весь вхід був чітко згрупований і явний, а також всі побічні ефекти були згруповані разом і явні, про це зазвичай говорять люди, коли говорять побічний ефект, чистий тощо.

Оскільки найбільш корисним є групування побічних ефектів та їх явність, інколи люди це означатимуть лише це, і розрізнити це, кажучи, що це не є чистим, але все-таки "побічним ефектом". Але побічний ефект є відносно припущеного "призначеного первинного ефекту", тому це контекстний термін. Це я знаходжу рідше, хоча дивно, що про це багато говорять.

Нарешті, idempotent означає, що викликати цю функцію багато разів з однаковими входами (неважливо, звідки вони беруться) завжди призведе до однакових ефектів (побічний ефект чи ні).


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

2

У програмуванні побічним ефектом є те, коли процедура змінює змінну за межами її області. Побічні ефекти не залежать від мови. Є деякі класи мов, які спрямовані на усунення побічних ефектів (чисто функціональні мови), але я не впевнений, чи є такі, які потребують побічних ефектів, але я можу помилитися.

Наскільки мені відомо, немає внутрішніх і зовнішніх побічних ефектів.


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

Я думаю, що деякі мови програмування до 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

@Charles - я згоден, хоча оригінальний приклад був не таким зрозумілим, як нинішній.
ChaosPandion

@Amir - Ну, це дійсно залежить від мови. Якби це 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.Потрібно все-таки бути 15 ... але це не так.

Виклик функції мав побічний ефект від зміни значення Seed.


10
Побічні ефекти не потрібно приховувати. Ви думаєте про розмовне або медичне використання; в програмуванні побічний ефект просто стосується зміни певного стану.
Aaronaught

1
Друк на консолі є побічним ефектом. Це не приховано. З Вікіпедії : "В інформатиці функція або вираз мають побічний ефект, якщо, крім повернення значення, воно також змінює деякий стан або має помітну взаємодію з функціями виклику або зовнішнім світом ".

Побічні ефекти - це те, як нефункції (тобто процедури) виконують будь-яку роботу. X = 1; X = Y (10) - дві чисті функції. Коли ви виходите за межі сфери "х = що завгодно", чи записувати вихід на екран | драйвер | принтер | під керівництвом чи читати вхід за межами формату "х = у" або просто змінювати значення змінної з однієї речі на іншу , це побічний ефект.
Джеймс К

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

Як і зрозуміти 0, спершу ви повинні зрозуміти 1: Щоб зрозуміти побічні ефекти, ви повинні спочатку зрозуміти функції.
Джеймс К
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.