Czy poniższe czynności są czystą funkcją?
function test(min,max) {
return Math.random() * (max - min) + min;
}
Rozumiem, że czysta funkcja spełnia następujące warunki:
- Zwraca wartość obliczoną na podstawie parametrów
- Nie wykonuje żadnej pracy poza obliczaniem wartości zwracanej
Jeśli ta definicja jest poprawna, czy moja funkcja jest funkcją czystą? A może moje rozumienie definicji czystej funkcji jest nieprawidłowe?
javascript
function
pure-function
Kiwi Rupela
źródło
źródło
Math.random()
co zmienia stan RNG.test(a,b)
Zawsze zwraca ten sam obiektRandom(a,b)
(który może reprezentować różne konkretne liczby)? Jeśli zachowaszRandom
symbolikę, jest ona czysta w klasycznym sensie, jeśli ocenisz ją wcześnie i wpiszesz w liczby, być może w ramach optymalizacji, funkcja nadal zachowuje pewną „czystość”.Odpowiedzi:
Nie, nie jest. Przy tych samych danych wejściowych ta funkcja zwróci różne wartości. A potem nie możesz zbudować „tabeli”, która odwzorowuje wejście i wyjście.
Z artykułu na Wikipedii dotyczącego funkcji Pure :
Inną rzeczą jest to, że czystą funkcję można zastąpić tabelą, która reprezentuje mapowanie z wejścia i wyjścia, jak wyjaśniono w tym wątku .
Jeśli chcesz przepisać tę funkcję i zmienić ją na czystą funkcję, powinieneś również przekazać wartość losową jako argument
a następnie nazwij to w ten sposób (na przykład z 2 i 5 jako min i max):
źródło
Math.random
?Math.random
produkcji); aby był czysty, musiałbyś w jakiś sposób zapisać bieżący stan RNG, ponownie go ustawić, wywołaćMath.random
i przywrócić do poprzedniego stanu.Prosta odpowiedź na Twoje pytanie brzmi:
Math.random()
narusza zasadę nr 2.Wiele innych odpowiedzi tutaj wskazywało, że obecność
Math.random()
oznacza, że ta funkcja nie jest czysta. Ale myślę, że warto powiedzieć, dlaczegoMath.random()
skazi funkcje, które go używają.Podobnie jak wszystkie generatory liczb pseudolosowych,
Math.random()
zaczyna się od wartości „ziarna”. Następnie używa tej wartości jako punktu wyjścia dla łańcucha niskopoziomowych manipulacji bitami lub innych operacji, które powodują nieprzewidywalne (ale nie w rzeczywistości losowe ) dane wyjściowe.W JavaScript zaangażowany proces jest zależny od implementacji i w przeciwieństwie do wielu innych języków JavaScript nie zapewnia możliwości wyboru ziarna :
Dlatego ta funkcja nie jest czysta: JavaScript zasadniczo używa niejawnego parametru funkcji, nad którym nie masz kontroli. Odczytuje ten parametr z danych obliczonych i przechowywanych w innym miejscu, a zatem narusza zasadę nr 2 w Twojej definicji.
Jeśli chcesz, aby była to czysta funkcja, możesz użyć jednego z alternatywnych generatorów liczb losowych opisanych tutaj . Zadzwoń do tego generatora
seedable_random
. Pobiera jeden parametr (ziarno) i zwraca „losową” liczbę. Oczywiście ta liczba wcale nie jest przypadkowa; jest wyjątkowo określona przez nasienie. Dlatego jest to czysta funkcja. Wynikseedable_random
jest tylko „losowy” w tym sensie, że przewidywanie wyniku na podstawie danych wejściowych jest trudne.Czysta wersja tej funkcji musiałaby mieć trzy parametry:
Dla dowolnej potrójnej
(min, max, seed)
wartości zawsze zwróci to ten sam wynik.Zwróć uwagę, że jeśli chcesz, aby wynik
seedable_random
był naprawdę losowy, musisz znaleźć sposób na losowanie ziarna! I jakakolwiek strategia, której użyłeś, nieuchronnie nie byłaby czysta, ponieważ wymagałaby od ciebie zebrania informacji ze źródła spoza twojej funkcji. Jak przypominają mi mtraceur i jpmc26 , obejmuje to wszystkie podejścia fizyczne: sprzętowe generatory liczb losowych , kamery internetowe z osłonami obiektywów , kolektory szumów atmosferycznych - nawet lampy lawowe . Wszystko to wiąże się z wykorzystaniem danych obliczonych i przechowywanych poza funkcją.źródło
Math.random
nie używała PRNG, ale zamiast tego została zaimplementowana przy użyciu sprzętowego RNG? Sprzętowy RNG tak naprawdę nie ma stanu w normalnym sensie, ale generuje wartości losowe (a zatem wyjście funkcji jest nadal różne niezależnie od wejścia), prawda?Czysta funkcja to funkcja, w której wartość zwracana jest określana tylko przez jej wartości wejściowe, bez obserwowalnych skutków ubocznych
Używając Math.random, określasz jego wartość na podstawie czegoś innego niż wartości wejściowe. To nie jest czysta funkcja.
źródło
źródło
Nie, to nie jest czysta funkcja, ponieważ jej wyjście nie zależy tylko od podanego wejścia (Math.random () może wyprowadzić dowolną wartość), podczas gdy czyste funkcje powinny zawsze zwracać tę samą wartość dla tych samych danych wejściowych.
Jeśli funkcja jest czysta, można bezpiecznie zoptymalizować wiele wywołań z tymi samymi danymi wejściowymi i po prostu ponownie wykorzystać wynik wcześniejszego wywołania.
PS, przynajmniej dla mnie i dla wielu innych, dzięki redukcjix popularność określenia czysta funkcja stała się popularna. Prosto z dokumentów Redux :
źródło
Z matematycznego punktu widzenia Twój podpis nie jest
ale
gdzie
environment
jest w stanie dostarczyć wynikiMath.random()
. Generowanie wartości losowej powoduje mutację środowiska jako efekt uboczny, więc zwracasz także nowe środowisko, które nie jest równe pierwszemu!Innymi słowy, jeśli potrzebujesz jakichkolwiek danych wejściowych, które nie pochodzą z argumentów początkowych (
<number, number>
część), to musisz mieć zapewnione środowisko wykonawcze (które w tym przykładzie zapewnia stan dlaMath
). To samo dotyczy innych rzeczy wymienionych w innych odpowiedziach, takich jak I / O lub takie.Analogicznie można zauważyć, że w ten sposób można przedstawić programowanie obiektowe - jeśli powiemy np
to faktycznie używamy
z obiektem, którego metoda została wywołana, będąc częścią środowiska. A dlaczego
SomeClass
część wyniku? Ponieważsomething
stan również mógł się zmienić!źródło
test: <environment, number, number> -> <environment, number>
powinno byća.F(b, c)
może być postrzegany jako cukier syntaktycznyF(a, b, c)
ze specjalną regułą do wysłania do przeciążonych definicji wF
oparciu o typa
(tak właściwie przedstawia to Python). Alea
nadal jest to wyraźne w obu notacjach, podczas gdy środowisko w funkcji innej niż czysta nigdy nie jest wspomniane w kodzie źródłowym.Czyste funkcje zawsze zwracają tę samą wartość dla tego samego wejścia. Czyste funkcje są przewidywalne i referencyjnie przezroczyste, co oznacza, że możemy zastąpić wywołanie funkcji zwróconym wynikiem i nie zmieni to działania programu.
https://github.com/MostlyAdequate/mostly-adequate-guide/blob/master/ch3.md
źródło
Oprócz innych odpowiedzi, które poprawnie wskazują, jak ta funkcja jest niedeterministyczna, ma ona również efekt uboczny: spowoduje, że przyszłe wywołania funkcji
math.random()
zwrócą inną odpowiedź. A generator liczb losowych, który nie ma tej właściwości, generalnie wykonuje pewnego rodzaju operacje we / wy, na przykład odczytuje z losowego urządzenia dostarczonego przez system operacyjny. Albo jest verboten dla czystej funkcji.źródło
Nie, nie jest. W ogóle nie możesz znaleźć wyniku, więc tego fragmentu kodu nie można przetestować. Aby kod był testowalny, musisz wyodrębnić komponent, który generuje liczbę losową:
Teraz możesz mockować generator i poprawnie przetestować kod:
A w kodzie „produkcyjnym”:
źródło
util.Random
, który można zainicjować na początku przebiegu testowego, aby powtórzyć stare zachowanie lub dla nowego (ale powtarzalnego) przebiegu. Jeśli korzystasz z wielu wątków, możesz to zrobić w głównym wątku i użyć tegoRandom
do zainicjowania powtarzalnych lokalnych wątkówRandom
. Jednak, jak rozumiem,test(int,int,Random)
nie jest uważany za czysty, ponieważ zmienia stanRandom
.Czy byłbyś w porządku z następującymi:
być równoważne z
?
Widzisz, definicja pure to funkcja, której wyjście nie zmienia się z niczym innym niż danymi wejściowymi. Gdybyśmy powiedzieli, że JavaScript miał sposób, aby oznaczyć funkcję jako czystą i skorzystać z tego, optymalizator mógłby przepisać pierwsze wyrażenie jako drugie.
Mam z tym praktyczne doświadczenie. Serwer SQL wolno
getdate()
inewid()
funkcje „czystych” i optymalizator będzie DeDupe połączeń do woli. Czasami robiłoby to coś głupiego.źródło