Dlaczego Today () jest przykładem nieczystej funkcji?

38

Wydaje się, że czytając coś takiego jak ten artykuł Wikipedii o „czystych funkcjach” , wymieniają Today()jako przykład nieczystej funkcji, ale wydaje mi się to dość czyste. Czy dlatego, że nie ma formalnego argumentu wejściowego? Dlaczego faktyczna pora dnia nie jest traktowana jako „wejście do funkcji”, w którym to przypadku, jeśli podałeś jej to samo wejście, tj. Wykonałeś today()dwa razy w tym samym czasie lub cofnąłeś się w czasie, aby wykonać je ponownie (być może hipotetyczny: )), wynik będzie taki sam. Today()nigdy nie daje ci losowej liczby. zawsze daje ci porę dnia.

Artykuł w Wikipedii mówi „w różnych momentach da to różne wyniki”, ale to tak, jakby powiedzieć, że dla różnych x sin(x)da różne proporcje. I sin(x)jest ich przykładem czystej funkcji.

Ćwiek
źródło
8
Jeśli zdałbyś porę dnia, co zrobiłaby ta funkcja?
JB King
1
Spodziewałbym się, że da ci to porę dnia. (nie jest to najbardziej przydatna funkcja). Ale nie ma w tym żadnego argumentu, który moim zdaniem jest źródłem odpowiedzi.
Brad
3
Czy potrafisz przewidzieć jego wynik (na podstawie podanych parametrów wejściowych)?
Daniel B
1
@DanielB Nie ma mocy predykcyjnej dla nieobecnego / zerowego parametru wejściowego, który się okazuje. Jedyne, co mogę zrobić, to spojrzeć na zegarek na rękę (jk mój telefon komórkowy).
Brad
„Dlaczego rzeczywista pora dnia nie jest traktowana jako„ dane wejściowe do funkcji ”„ Zasadniczo jest to problem, który monady próbują rozwiązać. Czyste funkcje mogą opierać się wyłącznie na ich danych wejściowych i nie mogą powodować żadnych skutków ubocznych. Jeśli uczynisz „stan świata przede mną” wkładem i „stan świata za mną” częścią wartości zwrotnej i przekażesz te stany świata poprzez swój program, możesz być znowu czysty.
Sean McSomething

Odpowiedzi:

103

Czy dlatego, że nie ma formalnego argumentu wejściowego?

Jest tak, ponieważ wynik zależy od czegoś, co nie jest wejściem, a mianowicie od bieżącego czasu.

Dlaczego faktyczna pora dnia nie jest traktowana jako „wejście do funkcji”

Ponieważ nie przekazałeś go jako parametru. Jeśli podasz go jako parametr, funkcja stanie się funkcją tożsamości w datach, co jest dość bezużyteczne. Celem całej Today()funkcji jest wyprowadzenie czegoś, co zależy od zewnętrznej i ciągle zmieniającej się wartości (czasu).

Zaletą czystych funkcji jest to, że ich zachowanie jest absolutnie powtarzalne i deterministyczne, co ułatwia posiadanie formalnych dowodów i twardych gwarancji. Zawsze robią to samo. Today()jest wręcz przeciwnie: zawsze (uwzględniając ziarnistość czasu) robi coś innego.

Michael Borgwardt
źródło
2
Tak więc, chociaż czas rzeczywistości jest rodzajem danych wejściowych, ponieważ nie jest podawany jako dane wejściowe i znajduje się poza kontrolą funkcji (zarówno wewnętrznie dla funkcji, jak i poza kontrolą osoby dzwoniącej Today()), Today()staje się nieczysty. Ta Today()funkcja może być trochę głupim przykładem. Bardziej odpowiednia może być jakaś Count()funkcja. Biorąc pod uwagę tę samą liczbę elementów do zliczenia Count(), zawsze zwróci tę samą liczbę, ale ponieważ jest to poza zakresem Count(), jest nieczysta.
Brad
1
@brad, który jest nieco szarym obszarem - istnieje domyślny rzeczywisty argument - tablica lub lista. Biorąc pod uwagę niezmienną listę i ten sam argument za każdym razem, zawsze zwróci tę samą wartość.
Maks.
34
„czas rzeczywistości jest swego rodzaju wkładem” - tak; w rzeczywistości stan globalny jest domyślnie dostępny (tj. „rodzaj danych wejściowych”) dla wszystkich funkcji, ale jeśli zależą od tego wyniku , są nieczyste!
AakashM
4
@Brad count()w większości języków programowania jest zdecydowanie czysty. Ma wyraźną wartość wejściową: kolekcję, której liczbę chcesz. Nie daj się pomylić składni takiej jak myCollection.count(); to tylko cukier count(myCollection).
Andres F.,
Świetna odpowiedź jak zawsze, ale nie obejmuje ona niezmiennych niezmiennych zmiennych. Nie są danymi wejściowymi do funkcji - nie są przekazywane jako parametr - ale funkcja zależy od nich, nawet jeśli nadal jest względnie przezroczysta.
24

sin(x)zawsze zwraca tę samą wartość, o ile xpozostaje taka sama. Today()mogą zwracać różne wyniki w czasie, ponieważ zależy to od wartości poza twoją kontrolą . Na przykład, jeśli coś poza kontrolą twojego programu zmieni wnętrze systemu $current_datetime podczas jego działania, Today()nagle przyniesie inne wyniki.

FrustratedWithFormsDesigner
źródło
„zawsze zwróci inną wartość” to trochę ... nieczyste sformułowanie. Wikipedia mówi „zwraca bieżący dzień tygodnia”, co oznacza, że ​​wartości uzyskane w poniedziałki nie będą się różnić
gnat
7
@gnat: Prawda, chyba że coś zewnętrznego w twoim programie zmieniło wewnętrzny kalendarz twojego komputera, tak że nagle pomyślał, że to czwartek. Następnie wywołanie Today()zwróci „czwartek” w poniedziałek.
FrustratedWithFormsDesigner
3
@gnat Cóż, nie zawsze zwróci inną wartość (nie ma prawie żadnej przydatnej funkcji, która by to zrobiła ). Jednak, podobnie jak większość nieczystych funkcji, wartość zwracana może się różnić nawet podczas wykonywania pojedynczego programu (np. Jeśli działa przez noc).
3
@delnan: Tak, to zmora autorów naiwnych skryptów baz danych! : P „Ale JAK MOGŁO pominąć 300 rekordów? Skrypt działał dobrze, kiedy testowałem go wczoraj rano!”
FrustratedWithFormsDesigner
@delnan to na pewno. Zwróciłem tylko uwagę, że użycie zawsze w początkowym brzmieniu (poprawione w aktualnej wersji odpowiedzi na mógł ) było nieco nieprecyzyjne
gnat
13

Today () jest nieczystą funkcją, ponieważ jej wynik zależy od czegoś, czego jej nie dajesz; konkretnie aktualny czas systemowy. Dlatego jego wynik nie jest deterministyczny, jeśli opiera się wyłącznie na danych wejściowych dostarczonych podczas wywołania.

Byłaby to czysta funkcja int Add(int a, int b) {return a + b;}. Funkcja działa wyłącznie z tym, co została podana, i nie wykorzystuje innych danych o stanie zewnętrznym. Naturalnym rezultatem tego jest to, że możesz Add(2,2)zdobyć 4 od teraz do końca czasu. Ponadto, ponieważ funkcja nie zmienia żadnego stanu zewnętrznego (nie ma „skutków ubocznych”), dodawanie (2) i 2 od teraz aż do końca czasu nie zmieni niczego innego w systemie, chyba że wtedy przypisz wynik funkcji do zmiennej lub w inny sposób użyj wartości do aktualizacji stanu (co nie jest operacją wykonywaną przez samą funkcję). Praktycznie wszystkie klasyczne operacje matematyczne są czystymi funkcjami i mogą być jako takie realizowane.

Z drugiej strony Today () może wygenerować tę samą wartość, gdy zostanie wywołany dwa razy z rzędu, ale nie, jeśli zostanie wywołany wielokrotnie przez kilka dni. Wynika to z faktu, że jest on zależny od danych o stanie zewnętrznym, które nie są dostarczane przez Ciebie jako parametr funkcji. W rezultacie niemożliwe jest kontrolowanie wyniku funkcji Today () w granicach programu. Będzie generować określoną wartość w danym dniu i nigdy nie wytworzy tej wartości w żadnym innym dniu, chyba że zmienisz zegar systemowy komputera, na którym jest on uruchomiony (zmiana zwykle występuje poza granicami programu).

Zanieczyszczona funkcja niekoniecznie jest złą rzeczą; nieczyste funkcje są wymagane, nawet w językach funkcjonalnych, do interakcji z czymkolwiek poza granicami programu, takimi jak magazyny danych, potoki komunikacyjne, wyświetlacze interfejsu użytkownika, urządzenia peryferyjne itp. Program, który nie wykonuje żadnej z tych czynności, jest programem jest to znacznie ograniczone pod względem użyteczności; Posunąłbym się nawet do tego, że nazwałbym taki program trywialnym, ponieważ bez jakiegokolwiek sposobu akceptacji danych wejściowych lub jakiejkolwiek drogi do poinformowania cię o jego wyniku, równie dobrze mógłby nic nie robić. Programy napisane w językach funkcjonalnych mogą mieć tylko dane wejściowe dostarczone przez środowisko wykonawcze i generować dane wyjściowe zgłaszane do środowiska wykonawczego bez żadnych wyraźnie zdefiniowanych nieczystych metod, ale dzieje się tak, ponieważ środowisko wykonawcze wyodrębnia wszystkie te nieczyste szczegóły pracy w niedoskonałym systemie komputerowym,

Po prostu Bardzo Dobrą Rzeczą jest wiedzieć, które z funkcji, których używasz, są czyste, a które nie, abyś mógł podejmować trafne decyzje dotyczące ich użycia. Zanieczyszczone funkcje, ponieważ robią rzeczy lub są zależne od rzeczy, które nie są oczywiste z ich użycia, mogą zachowywać się nieprzewidywalnie, biorąc pod uwagę tylko wiedzę o użytkowaniu. Konieczna jest dalsza znajomość celu funkcji, a tym samym tego, czego potrzebuje od stanu zewnętrznego lub do czego służy, aby ustawić system, który używa go w spójnym stanie, a tym samym oczekiwać wyniku deterministycznego.

KeithS
źródło
8

Wydaje się dość oczywiste, że ta funkcja nie spełnia pierwszego testu czystości podanego na samym początku tej strony:

  1. Funkcja zawsze ocenia tę samą wartość wyniku, biorąc pod uwagę te same wartości argumentu. Wartość wyniku funkcji nie może zależeć od żadnych ukrytych informacji lub stanów, które mogą się zmieniać w trakcie wykonywania programu lub między różnymi wykonaniami programu, ani też nie może zależeć od jakichkolwiek zewnętrznych danych wejściowych z urządzeń I / O.

Zauważ, że ponieważ nie przyjmuje żadnych argumentów, istnieje tylko jeden możliwy zestaw wartości argumentów - pusty zbiór. Ta funkcja może i zwraca różne wyniki dla tych samych „wartości argumentów”.

Ponadto, wartość wynik funkcji jest uzależnione od „ukrytej ... stanu, który może się zmienić w miarę postępu realizacji programu”. Więc kolejna porażka.

AakashM
źródło
@ JörgWMittag Nie jestem pewien, gdzie twierdzę, że funkcja bez argumentów nie może zwrócić wartości.
AakashM
Pierdnięcie mózgu. Przeczytałem „istnieje tylko jeden możliwy zestaw wartości zwracanych ”.
Jörg W Mittag
8

() => 1byłaby czystą funkcją, ponieważ zawsze zwraca 1. Today()może zwrócić „poniedziałek” lub „wtorek” lub prawie dowolną inną wartość.

Innym sposobem myślenia o tym jest to, że czyste funkcje nie zależą od stanu. Świat jest zwykle uważany za stan. Musisz znać stan rzeczywistości, aby wiedzieć, jaki jest dzisiaj dzień.

Jednak nie musisz wiedzieć nic specjalnego o stanie świata, aby wiedzieć, co sin(x)jest. I zawsze wezwanie do sin(x)danej xzwraca tę samą wartość.

Guvante
źródło
Wikipedia mówi „zwraca aktualny dzień tygodnia”, co oznacza, że może powrócić poniedziałek, wtorek itp ale nie „1/23/2013” ani „1/24/2013”
gnat
7
@gnat: Zaktualizowano, ale różnica nie była tak naprawdę istotna.
Guvante
2

Date(timestamp)byłaby czystą funkcją. Z powodu jego idempotencji. A ponieważ nie byłoby żadnego efektu ubocznego.

Today()może różnić się w zależności od tego, kiedy do niego zadzwonisz. To czyni go nieczystym. To nie jest idempotentne. Nie ma to jednak żadnego efektu ubocznego, ale to nie czyni go czystym.

Florian Margaine
źródło
2

Oto mały pseudo kod, o którym myślę omawiając czyste funkcje

newValue = Function();
while(true)
{
   oldValue = newValue;
   newValue = Function();
   assert( newValue == oldValue );
}

Jeśli działa to w nieskończoność i nigdy nie może wywołać potwierdzenia, jest to czysta funkcja. Co więcej, jeśli masz funkcję, która używa argumentów, to mała modyfikacja ....

oldValue = Function( importantVariableToYourApp );
newValue = Function( importantVariableToYourApp );
assert( newValue == oldValue );

Jeśli możesz użyć tego po każdym przypisaniu zmiennej w swojej aplikacji, a to nie zmienia wyników w aplikacji i nigdy nie może zawieść potwierdzenia, to jest to czysta funkcja.

Drake Clarris
źródło
2

Po pierwsze, nie ma czegoś takiego jak funkcja bez argumentów (lub tablica bez indeksów lub mapa bez kluczy). Jest to cecha definiująca funkcję mapowania jednej lub więcej wartości argumentów na inną wartość.

Dlatego też todayalbo wcale nie jest funkcją, a zatem nie jest funkcją czystą. Lub możemy zinterpretować składnię

today()

trochę, żeby to znaczyło

today   ()      -- today, applied to the value ()

Na przykład w Haskell byłoby to prawidłowe:

data Day = Mon | Tue | Wed | Thu | Fri | Sat | Sun deriving Show
today :: () -> Day
today () = ....?
main = print (today())

ponieważ istnieje typ () z pojedynczą wartością ().

Pytanie tylko, jak todayobliczyć dzień tygodnia, jeśli ma tylko ()? Jest to po prostu niemożliwe bez odczytu timera systemowego, bezpośrednio lub za pomocą nieczystych funkcji pomocnika.

Timer systemowy jest doskonałym przykładem stanu globalnego.

Ingo
źródło
1

Problem today()polega na tym, że może dać inny wynik, jeśli zostanie wywołany dwa lub więcej razy w funkcji.

Oto przykład kodu, który może wprowadzić błąd.

function doSomething(when)
{
     if(today() == when)
     {
           // open a resource or create a temp file.....
     }

     // do some other work

     if(today() == when)
     {
           // close the resource or delete temp file.....
     }
}

Jest to możliwe w powyższym przykładzie. To, że drugie ifzdanie nie zostanie wykonane. Nawet jeśli pierwszy. Pozostawienie zasobu w złym stanie.

Reactgular
źródło
1

Aby być czystą funkcją, zapewnienie tych samych parametrów musi dawać ten sam wynik za każdym razem.

Za każdym razem, gdy dzwonimy Today(), zapewniamy te same parametry (brak), ale niekoniecznie uzyskujemy ten sam wynik (poniedziałek, wtorek itp.).

Zantier
źródło
4
wydaje się to jedynie powtórzyć punkt poczyniony i wyjaśniony w najwyższej odpowiedzi, która została opublikowana około dwa lata temu. Nie warto podrzucać dwuletniego pytania takimi treściami
komnata
1
Nie jestem zbyt obeznany z działaniem wymiany stosów, ale doszedłem do wniosku, że skoro było to w najczęściej zadawanych pytaniach, to już zostało zderzone. Jeśli chodzi o powtórzenie, pamiętam, że czytałem na meta, że ​​pomocne może być uzyskanie wielu podobnych odpowiedzi. Czuję, że mój jest zwięzły i potencjalnie pomocny.
Zantier