Co to jest „efekt uboczny?”

87

Nie do końca zrozumiałem pojęcie efektu ubocznego.

  • Jaki jest efekt uboczny w programowaniu?
  • Czy to zależy od języka programowania?
  • Czy istnieje coś takiego jak zewnętrzne i wewnętrzne skutki uboczne?

Podaj przykład przyczyn, które powodują skutki uboczne.

Amir Rezaei
źródło
7
Brzmi bardzo jak praca domowa.
gnasher729
3
@ gnasher729, kogo to obchodzi, jest PRAWDOPODOBNIE przydatne :)
Charlie Parker

Odpowiedzi:

108

Efektem ubocznym odnosi się po prostu do zmiany swego rodzaju stanu, na przykład -:

  • Zmiana wartości zmiennej;
  • Zapisywanie niektórych danych na dysk;
  • Włączanie lub wyłączanie przycisku w interfejsie użytkownika.

W przeciwieństwie do tego, co niektórzy ludzie mówią:

  • Efekt uboczny nie musi być ukryty ani nieoczekiwany (może być, ale nie ma to nic wspólnego z definicją, ponieważ dotyczy informatyki);

  • Efekt uboczny nie ma nic wspólnego z idempotencją. Funkcja idempotentna może mieć skutki uboczne, a funkcja niebędąca idempotentna może nie mieć żadnych skutków ubocznych (takich jak uzyskanie bieżącej daty i godziny systemowej).

To naprawdę bardzo proste. Efekt uboczny = coś gdzieś zmienić.

PS Jak zauważa komentator Benjol, kilka osób może łączyć definicję efektu ubocznego z definicją czystej funkcji , która jest funkcją, która jest (a) idempotentna i (b) nie ma skutków ubocznych. Jedno nie implikuje drugiego w ogólnej informatyce, ale funkcjonalne języki programowania zwykle mają tendencję do egzekwowania obu ograniczeń.

Aaronaught
źródło
38
Określenie „efekt uboczny” sprawia, że brzmi to jak coś innego jest zmiana innych niż to, co było zamierzone. W medycynie lek będzie miał główny efekt zmniejszenia bólu, a czasem efektem ubocznym spowodowania krwawień z nosa, zawrotów głowy itp. Celem tego leku nie jest wywoływanie krwawień z nosa, ale czasami dzieje się tak niezamierzony dodatkowy wynik.
FrustratedWithFormsDesigner
15
@ Sfrustrowany: +1. Ilekroć widzę ten termin, nie mogę przestać się zastanawiać, czy zwolennicy FP nie wybrali go, by stworzyć dokładnie tę subtelnie złowrogą konotację.
Mason Wheeler
6
@Mason Wheeler. Istniał na długo przed FP. I to nie jest subtelnie złowroga konotacja. To całkowicie złe zło i zawsze tak było. Przez trzy dekady, które kodowałem, stwierdzenie „szyfrowanie przypisań” - efekt uboczny - niepokoiło ludzi. Proste, stare stwierdzenie przydziału jest o wiele łatwiejsze do poradzenia sobie.
S.Lott
7
@Mason Wheeler W C ++a. Nie wygląda jak zadanie. b = ++a;ma dwa skutki uboczne. Oczywiste i krypto-przypisanie a. Jest to coś, co jest efektem ubocznym, który (dla niektórych) jest pożądany. Ale został nazwany efektem ubocznym w całej mojej karierze, aby nie był subtelny.
S.Lott
5
@Zachary, proszę zobaczyć ostatni punkt w mojej odpowiedzi. To, o czym mówisz, to idempotentne zachowanie (lub jego brak). To nic nie mówi o skutkach ubocznych. Sprawdzanie zegara systemowego nie jest efektem ubocznym; w rzeczywistości jakakolwiek funkcja lub metoda poprzedzona słowem „get” to taka, której można zasadnie oczekiwać, że nie wywoła żadnych skutków ubocznych.
Aaronaught
36

Mówi się, że każda operacja, która zmienia stan komputera lub wchodzi w interakcję ze światem zewnętrznym, ma efekt uboczny. Zobacz Wikipedia na temat efektów ubocznych .

Na przykład ta funkcja nie ma skutków ubocznych. Jego wynik zależy tylko od argumentów wejściowych i nic nie zmienia stanu programu ani jego środowiska, gdy jest wywoływane:

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

Natomiast wywołanie tych funkcji da różne wyniki w zależności od kolejności, w jakiej je wywołujesz, ponieważ zmieniają coś o stanie komputera:

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

Ta funkcja ma efekt uboczny zapisywania danych na wyjściu. Nie wywołujesz funkcji, ponieważ chcesz jej wartość zwracaną; nazywacie to, ponieważ chcecie, aby miało to wpływ na „świat zewnętrzny”:

int Write(const char* s) { return printf("Output: %s\n", s); }
Kristopher Johnson
źródło
1
To dobra definicja, ale nie jestem szalony z powodu opracowania - tak jak w odpowiedzi Thorbjørna, część z nich wydaje się mieszać kwestię efektów ubocznych z kwestią idempotentnych funkcji; jak Writepokazuje twój przykład, wywoływanie efektów ubocznych nie oznacza, że ​​funkcja kiedykolwiek zmienia swoje dane wyjściowe w odniesieniu do swoich danych wejściowych, ani nawet że ich wyniki zależą w ogóle od danych wejściowych.
Aaronaught
6
Tu nie chodzi o bycie idempotentnym. Fakt, że produkuje dane wyjściowe oznacza, że ​​ma efekt uboczny.
Kristopher Johnson
W niektórych systemach wywołanie square(x)może spowodować załadowanie modułu, w którym zdefiniowano funkcję, z dysku. Czy należy to uznać za efekt uboczny? W końcu to męskie, że (pierwsze) wywołanie zajmuje nieoczekiwanie długo, że zwiększa się użycie pamięci RAM itp.
Hagen von Eitzen
1
@HagenvonEitzen Każda operacja faktycznie wprowadza zmiany w stanie komputera (rejestry procesora, pamięć, zużycie energii, ciepło itp.). „Efekt uboczny” zwykle odnosi się do wyimaginowanego, wyidealizowanego środowiska wykonawczego, w którym nic się nie zmienia w tym środowisku, chyba że program je wyraźnie zmieni. Ale jeśli dzwonisz, square(x) ponieważ chcesz zmienić stan komputera zewnętrznego, możesz uznać to za efekt uboczny.
Kristopher Johnson
Dla mnie pierwsza ilustracja ma doskonały sens. Jednak drugi mniej. Uważam, że efekt uboczny powinien zostać zdefiniowany w odniesieniu do określonego środowiska / zakresu. Jeśli weźmiesz pod uwagę cały wszechświat, nie ma czegoś takiego jak efekt uboczny. Nawet jeśli ograniczysz to do komputera, twoja funkcja wpłynie na inne procesy, ponieważ procesor nie będzie zachowywał się tak samo. Jeśli ograniczysz zakres do rzeczy dostępnych w lokalnym zasięgu funkcji, mamy coś do omówienia.
funk7
20

Myślę, że istniejące odpowiedzi są całkiem dobre. Chciałbym rozwinąć niektóre aspekty, które IMO nie były wystarczająco zestresowane.

W matematyce funkcja jest po prostu odwzorowaniem krotki wartości na wartość. Tak więc, biorąc pod uwagę funkcję fi wartość x, f(x)zawsze będzie ten sam wynik y. Można również wymienić f(x)się ywszędzie w wyrażeniu i nic się nie zmieni.

W wielu językach programowania funkcja (lub procedura) nazywana jest konstrukcją (fragmentem kodu), którą można wykonać, ponieważ:

  1. Oblicza funkcję w sensie matematycznym, tzn. Przy danych wartościach wejściowych zwraca wynik lub
  2. Daje pewien efekt, np. Drukuje coś na ekranie, zmienia wartość w bazie danych, wystrzeliwuje pociski, śpi przez 10 sekund, wysyła SMS-a.

Efekty można więc powiązać ze stanem, ale także z innymi aspektami, takimi jak wystrzelenie pocisku lub wstrzymanie egzekucji na kilka sekund.

Termin efekt uboczny może wydawać się negatywny, ale zwykle efekt wywołania funkcji jest głównym celem samej funkcji. Podejrzewam, że skoro termin funkcja był pierwotnie używany w matematyce, obliczenie wartości jest uważane za główny efekt funkcji, podczas gdy wszelkie inne efekty są uważane za skutki uboczne . Niektóre języki programowania używają terminu procedura, aby uniknąć pomyłek z funkcjami w sensie matematycznym.

Zauważ, że

  1. Niektóre procedury są przydatne zarówno ze względu na ich wartość zwrotną, jak i ich efekt uboczny.
  2. Niektóre procedury obliczają tylko wartość wyniku i nie mają innych efektów. Są często nazywane funkcjami czystymi, ponieważ wszystko, co robią, to obliczają funkcję w sensie matematycznym.
  3. Niektóre procedury, np. sleep()W Pythonie, są użyteczne tylko dla ich (ubocznych) efektów,. Często są one modelowane jako funkcje, które zwracają specjalną wartość Nonelub unitlub ()lub…, co po prostu wskazuje, że obliczenia zakończyły się poprawnie.
Giorgio
źródło
2
Moim skromnym zdaniem powinna to być zaakceptowana odpowiedź. Pojęcie efektu ubocznego ma sens nawet pod względem funkcji matematycznych. Procedura ma na celu po prostu grupowanie zestawu instrukcji w uporządkowany sposób, umożliwiając jednocześnie wygodne przejście do tego zestawu z dowolnego miejsca iz powrotem. Nie ma pierwotnego zamierzonego efektu i efektów ubocznych. Można powiedzieć, że zgłoszenie wyjątku jest efektem ubocznym procedury, ponieważ łamie ona cel procedury, która polega na cofnięciu cię do miejsca, w którym przerwałeś i kontynuowaniu tam formy wykonania.
Didier A.,
4

Efekt uboczny występuje, gdy operacja ma wpływ na zmienną / obiekt, który jest poza zamierzonym użyciem.

Może się to zdarzyć, gdy wywołasz funkcję złożoną, której efektem ubocznym jest zmiana jakiejś zmiennej globalnej, nawet jeśli nie z tego powodu ją wywołałeś (być może wywołałeś ją, aby wyodrębnić coś z bazy danych).

Przyznaję, że mam problem z wymyśleniem prostego przykładu, który nie wygląda na całkowicie wymyślony, a przykłady z rzeczy, nad którymi pracowałem, są zbyt długie, aby je tutaj opublikować (a ponieważ jest to związane z pracą, prawdopodobnie i tak nie powinienem ).

Jednym z przykładów, które widziałem (jakiś czas temu), była funkcja, która otworzyła połączenie z bazą danych, jeśli połączenie było w stanie zamkniętym. Problem polegał na tym, że miał on zamknąć połączenie na końcu funkcji, ale programista zapomniał dodać ten kod. Tak więc tutaj wystąpił niezamierzony efekt uboczny: wywołanie procedury miało jedynie wykonać zapytanie, a efektem ubocznym było to, że połączenie pozostało otwarte, a jeśli funkcja zostanie wywołana dwa razy z rzędu, pojawi się błąd informujący, że połączenie było już otwarty.


Ok, skoro wszyscy podają teraz przykłady, myślę, że ja też;)

/*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;

Funkcja do_task_xma podstawowy wpływ powrocie wynik pewnych obliczeń i boczny efekt ewentualnie modyfikując zmienną globalną.

Oczywiście, który jest pierwotny i który jest efektem ubocznym, może być otwarty na interpretację i może zależeć od faktycznego użycia. Jeśli wywołam tę funkcję w celu modyfikacji globalnej i odrzucę zwróconą wartość, powiedziałbym, że modyfikacja globalnej jest głównym efektem.

FrustratedWithFormsDesigner
źródło
2
Nie sądzę, że jest to dobra uniwersalna definicja. Wielu programistów celowo używa konstruktów specjalnie dla ich efektu ubocznego.
CB Bailey,
@Charles: Wystarczająco uczciwy. W takim razie, jak byś to zdefiniował?
FrustratedWithFormsDesigner
2
Myślę, że @KristopherJohnson ma najjaśniejszą definicję. Wszystko, co zmienia ten stan programu lub jego środowiska lub daje efekt rzeczywistego świata, takie jak generowanie wyników.
CB Bailey,
@Charles Bailey: To nie zmienia definicji. Używanie rzeczy jako efektu ubocznego jest w porządku. Tak długo, jak rozumiesz, że występuje efekt uboczny. Nie zmienia to niczego w tej definicji.
S.Lott
1
@Sott: Definicja w tej odpowiedzi (tj. Akapit pierwszy) zawiera klauzulę: „poza zamierzonym użyciem”. Myślę, że mój komentarz był sprawiedliwy.
CB Bailey,
3

W informatyce mówi się, że funkcja lub wyrażenie ma efekt uboczny, jeśli zmienia jakiś stan lub ma zauważalną interakcję z funkcjami wywoływania lub światem zewnętrznym.

Z Wikipedii - Efekt uboczny

W sensie matematycznym funkcja jest odwzorowaniem między danymi wejściowymi a wyjściowymi. Zamierzonym efektem wywołania funkcji jest odwzorowanie danych wejściowych na wynik, który zwraca. Jeśli funkcja robi cokolwiek innego, nie ma znaczenia co, ale jeśli ma jakieś zachowanie, które nie mapuje danych wejściowych na dane wyjściowe, wiadomo, że takie zachowanie jest efektem ubocznym.

Mówiąc bardziej ogólnie, efektem ubocznym jest każdy efekt, który nie jest zamierzonym efektem projektanta konstrukcji.

Efektem jest wszystko, co wpływa na aktora. Jeśli wywołam funkcję, która wysyła mojej dziewczynie wiadomość tekstową zerwania, która wpływa na grupę aktorów, mnie, ją, sieć firmy komórkowej itp. Jedynym zamierzonym efektem wywołania funkcji bez skutków ubocznych jest funkcja aby zwrócić mi mapowanie z moich danych wejściowych. Więc dla:

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

Jeśli ma to być funkcja, jedyne, co powinien zrobić, to return void. Jeśli byłby wolny od skutków ubocznych, nie powinien tak naprawdę wysyłać wiadomości tekstowej.

W większości języków programowania nie ma konstrukcji dla funkcji matematycznej. Żaden konstrukt nie jest przeznaczony do takiego zastosowania. Dlatego większość języków mówi, że masz metody lub procedury. Z założenia są one w stanie wykonać znacznie więcej efektów. W potocznym języku programowania nikt tak naprawdę nie dba o to, czym jest metoda lub procedura, więc kiedy ktoś mówi, że ta funkcja ma efekt uboczny, oznacza to, że konstrukcja nie zachowuje się jak funkcja matematyczna. A kiedy ktoś mówi, że ta funkcja jest wolna od efektów ubocznych, oznacza to, że konstrukcja ta zachowuje się jak funkcja matematyczna.

Z definicji czysta funkcja jest zawsze wolna od skutków ubocznych. Czysta funkcja jest sposobem, aby powiedzieć, że ta funkcja, mimo że używa konstrukcji, która pozwala na więcej efektów, ma tylko taki sam efekt jak funkcja matematyczna.

Wzywam każdego, by powiedział mi, kiedy funkcja bez skutków ubocznych nie byłaby czysta. O ile pierwotny zamierzony efekt kontekstu zdania przy użyciu terminu czysty i wolny od skutków ubocznych nie jest skutkiem matematycznego zamierzonego efektu funkcji, to są one zawsze równe.

Jako takie, czasami, choć rzadziej, i uważam, że jest to rozróżnienie, w którym brakuje ludzi, a także wprowadzających w błąd ludzi (ponieważ nie jest to najczęstsze założenie) w przyjętej odpowiedzi, ale czasami zakłada się, że zamierzonym efektem funkcji programowania jest odwzorować dane wejściowe na dane wyjściowe, gdzie dane wejściowe nie są ograniczone do jawnych parametrów funkcji, ale dane wyjściowe są ograniczone do jawnej wartości zwracanej. Jeśli założymy, że jest to zamierzony efekt, to funkcja odczytująca plik i zwracająca inny wynik na podstawie tego, co znajduje się w pliku, jest nadal wolna od efektów ubocznych, ponieważ zezwalasz na dane wejściowe pochodzące z innych miejsc zamierzonego efektu.

Dlaczego to wszystko jest tak ważne?

Chodzi o kontrolę i utrzymanie go. Jeśli wywołujesz funkcję, która robi coś innego, a następnie zwraca wartość, trudno jest uzasadnić jej zachowanie. Będziesz musiał zajrzeć do funkcji, aby znaleźć właściwy kod, aby odgadnąć, co robi i potwierdzić jego poprawność. Idealną sytuacją jest to, że jest bardzo jasne i łatwe do zrozumienia, z jakiego wejścia korzysta funkcja, i że nie robi nic innego, niż zwracanie dla niej wyniku. Możesz to trochę rozluźnić i powiedzieć, że wiedza o tym, czego używa, nie jest tak pomocna, jak pewność, że nie robi nic innego, czego możesz nie być świadomy, a następnie zwrócić wartość, więc być może jesteś zadowolony z samego tylko egzekwowania że nie robi nic innego, ale mapuje dane wejściowe, bez względu na to, skąd je bierze, na dane wyjściowe.

W prawie wszystkich przypadkach celem programu jest uzyskanie efektów innych niż mapowanie rzeczy wchodzących do rzeczy wychodzących. Idea kontrolowania efektu ubocznego polega na tym, że możesz uporządkować kod w sposób łatwiejszy do zrozumienia i uzasadnienia. Jeśli połączysz wszystkie skutki uboczne w miejscu, które jest bardzo wyraźne i centralne, łatwo jest wiedzieć, gdzie szukać i ufać, że to wszystko, co się dzieje, nigdy więcej. Jeśli dane wejściowe są również bardzo wyraźne, pomaga to przetestować zachowanie dla różnych danych wejściowych i jest łatwiejsze w użyciu, ponieważ nie trzeba zmieniać danych wejściowych w wielu różnych miejscach, niektóre z nich mogą nie być oczywiste, po prostu aby dostać to, czego chcesz.

Ponieważ najbardziej pomocne w zrozumieniu, zrozumieniu i kontrolowaniu zachowania programu jest wyraźne pogrupowanie wszystkich danych wejściowych i jednoznaczne, a także zgrupowanie wszystkich skutków ubocznych i jednoznaczne, o tym mówią ludzie, kiedy mówią efekt uboczny, czysty itp.

Ponieważ najbardziej pomocne jest grupowanie skutków ubocznych i ich jawność, czasami ludzie będą to tylko oznaczać i rozróżniać, mówiąc, że nie jest czysty, ale nadal „wolny od skutków ubocznych”. Ale efekt uboczny jest związany z założonym „zamierzonym efektem pierwotnym”, więc jest to termin kontekstowy. To, co znajduję, jest rzadziej używane, choć o dziwo w tym wątku wiele się mówi.

Wreszcie, idempotent oznacza wielokrotne wywoływanie tej funkcji z tymi samymi danymi wejściowymi (nieważne, skąd pochodzą) zawsze skutkuje tymi samymi efektami (efekt uboczny lub nie).

Didier A.
źródło
Myślę, że dużym problemem w wyjaśnianiu skutków ubocznych jest to, że dopóki nie użyjesz języka takiego jak Ocaml lub Haskell, bardzo trudno jest uzasadnić programowanie bez skutków ubocznych (prawie!).
Jamie Strauss,
2

Podczas programowania efektem ubocznym jest zmiana procedury spoza zakresu. Efekty uboczne nie są zależne od języka. Istnieje kilka klas języków, które mają na celu wyeliminowanie skutków ubocznych (czysto funkcjonalne języki), ale nie jestem pewien, czy istnieją takie, które wymagają efektów ubocznych, ale mogę się mylić.

O ile mi wiadomo, nie ma wewnętrznych i zewnętrznych skutków ubocznych.

indyk1ng
źródło
Mówiąc ściślej, czysto funkcjonalne języki wyraźnie oddzielają kod wolny od skutków ubocznych od innych kodów, podczas gdy inne języki nie mają mechanizmu rozróżniania czystego i nieczystego kodu. Większość programów musi mieć skutki uboczne, aby były przydatne.
Giorgio
Myślę, że niektóre języki programowania pre-gui, takie jak MS-BASIC i QBasic, mogły być tak blisko języka „tylko efektów ubocznych”, jak to tylko możliwe. I tak, możesz mieć wewnętrzne i zewnętrzne skutki uboczne.
James K
0

Oto prosty przykład:

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

Definicja efektu ubocznego nie jest specyficzna dla programowania, więc po prostu wyobraź sobie skutki uboczne twojego meds lub jedzenia zbyt dużej ilości jedzenia.

ChaosPandion
źródło
Ale jeśli wiadomość pojawi się jako odniesienie i zmienisz wiadomość w swojej metodzie, może to być efekt uboczny. Mam rację?
Amir Rezaei
Fakt, że wyrażenie x++modyfikuje zmienną, xjest powszechnie uważany za efekt uboczny. Ta wartość wyrażenia jest wartością wstępną inkrementu x; jest to część wyrażenia niezwiązana z efektem ubocznym.
CB Bailey,
@Charles - Zgadzam się, chociaż oryginalny przykład nie był tak jasny jak obecny.
ChaosPandion
@Amir - Cóż, to naprawdę zależy od języka. Gdyby to był C #, nie byłoby to uważane za efekt uboczny.
ChaosPandion
@ChaosPandion: Osobiście się nie zgadzam. Oryginalny przykład był znacznie prostszy i jaśniejszy.
CB Bailey,
-2

Efektem ubocznym są rzeczy, które dzieją się w kodzie, które nie są oczywiście oczywiste.

Powiedzmy na przykład, że masz tę klasę

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

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

Kiedy początkowo tworzysz klasę, dajesz jej zalążek.

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

Nie znasz elementów wewnętrznych, po prostu oczekujesz, że otrzymasz losową wartość i możesz oczekiwać, że randomGenerator. Seed nadal będzie miał 15 ... ale tak nie jest.

Wywołanie funkcji miało efekt uboczny zmiany wartości Seed.

CaffGeek
źródło
10
Efekty uboczne nie muszą być ukryte. Myślisz o użyciu potocznym lub medycznym; w programowaniu efekt uboczny po prostu odnosi się do modyfikacji jakiegoś stanu.
Aaronaught
1
Drukowanie na konsoli to efekt uboczny. To nie jest ukryte. Z Wikipedii : „Mówi się, że w informatyce funkcja lub wyrażenie ma efekt uboczny, jeśli oprócz zwracania wartości modyfikuje także pewien stan lub ma zauważalną interakcję z funkcjami wywoływania lub światem zewnętrznym ”.
Skutki uboczne to sposób, w jaki funkcje (tj. Procedury) wykonują jakąkolwiek pracę. X = 1; X = Y (10) to dwie czyste funkcje. Kiedy wychodzisz poza sferę „x = cokolwiek”, czy zapisywać dane wyjściowe na ekranie | dysk | drukarka | led lub czytać dane wejściowe poza formatem „x = y”, czy po prostu zmieniać wartość zmiennej z jednej rzeczy na drugą , to efekt uboczny.
James K
Myślę, że „ukryty” oznacza nieoczywisty. Podobnie jak w x = f (y, z) x można założyć, że opiera się na y i z. Podczas gdy proc (x, y, z) nie mówi ci nic o tym, co się dzieje. Każda zmienna może być zmieniona lub żadna. Proc może być analogiczny do f lub całkowicie niezwiązany. Czysta funkcja ma jedną odpowiedź: „x”. Wyjdź poza to, to efekt uboczny. Całkowicie zamierzone, ale skutki uboczne.
James K
Aby zrozumieć 0, musisz najpierw zrozumieć 1: Aby zrozumieć skutki uboczne, musisz najpierw zrozumieć funkcje.
James K