Czysta funkcja - Pure function
W programowaniu komputerowym , wykorzystując czystą funkcją jest funkcja , która ma następujące właściwości:
- W funkcji wartości powrotne są takie same dla tych samych argumentów (brak zróżnicowania lokalnych zmiennych statycznych , zmiennych nielokalnych , modyfikowalnych argumentów odniesienia lub strumieni wejściowych ).
- Funkcja aplikacja nie ma skutków ubocznych (bez mutacji lokalnych zmiennych statycznych, zmiennych nielokalnych, modyfikowalnych argumentów referencyjnych lub strumieni wejścia / wyjścia).
Zatem czysta funkcja jest obliczeniowym odpowiednikiem funkcji matematycznej . Niektórzy autorzy, szczególnie z imperatywnej społeczności językowej, używają terminu „czysty” dla wszystkich funkcji, które właśnie posiadają powyższą właściwość 2 (omówione poniżej ).
Przykłady
Czyste funkcje
Poniższe przykłady funkcji C++ są czyste:
-
floor
, zwracając podłogę numeru; -
max
, zwracając maksymalnie dwie wartości. - funkcja f , zdefiniowana jako
Chociaż ten przykładowy kod wygląda, jakby nie był czysty, w rzeczywistości tak jest. Wartość of
void f() { static std::atomic<unsigned int> x = 0; ++x; }
x
można zaobserwować tylko wewnątrz innych wywołańf()
, a ponieważf()
nie przekazuje wartościx
swojemu otoczeniu, jest nie do odróżnienia od funkcjivoid f() {}
, która nic nie robi. Należy pamiętać, żex
jeststd::atomic
tak, że modyfikacje z wielu wątków wykonującychf()
jednocześnie nie doprowadzić do wyścigu danych , która ma niezdefiniowanej zachowanie w C i C ++.
Nieczyste funkcje
Następujące funkcje C++ są nieczyste, ponieważ brakuje im powyższej właściwości 1:
- ze względu na zmienność wartości zwrotu ze zmienną statyczną
int f() { static int x = 0; ++x; return x; }
- ze względu na zmienność wartości zwrotu ze zmienną nielokalną
Z tego samego powodu np. funkcja biblioteczna C++
int f() { return x; }
sin()
nie jest czysta, ponieważ jej wynik zależy od trybu zaokrąglania IEEE, który można zmienić w czasie wykonywania. - ze względu na zmienność zwracanej wartości ze zmiennym argumentem referencyjnym
int f(int* x) { return *x; }
- ze względu na zmienność wartości zwracanej ze strumieniem wejściowym
int f() { int x = 0; std::cin >> x; return x; }
Następujące funkcje C++ są nieczyste, ponieważ brakuje im powyższej właściwości 2:
- z powodu mutacji lokalnej zmiennej statycznej
void f() { static int x = 0; ++x; }
- z powodu mutacji zmiennej nielokalnej
void f() { ++x; }
- z powodu mutacji zmiennego argumentu referencyjnego
void f(int* x) { ++*x; }
- z powodu mutacji strumienia wyjściowego
void f() { std::cout << "Hello, world!" << std::endl; }
Następujące funkcje C++ są nieczyste, ponieważ nie mają obu powyższych właściwości 1 i 2:
- ze względu na zmienność wartości zwracanej z lokalną zmienną statyczną i mutacją lokalnej zmiennej statycznej
int f() { static int x = 0; ++x; return x; }
- ze względu na zmienność zwracanej wartości ze strumieniem wejściowym i mutacją strumienia wejściowego
int f() { int x = 0; std::cin >> x; return x; }
I/O w czystych funkcjach
We/wy jest z natury nieczyste: operacje wejściowe podważają przezroczystość referencyjną , a operacje wyjściowe powodują skutki uboczne. Niemniej jednak istnieje sens, w którym funkcja może wykonywać dane wejściowe lub wyjściowe i nadal być czysta, jeśli sekwencja operacji na odpowiednich urządzeniach we/wy jest modelowana jawnie jako zarówno argument, jak i wynik, a operacje we/wy są brane do nie powiedzie się, gdy sekwencja wejściowa nie opisuje operacji faktycznie wykonanych od momentu rozpoczęcia wykonywania programu.
Drugi punkt zapewnia, że jedyna sekwencja używana jako argument musi się zmieniać przy każdej akcji I/O; pierwszy umożliwia różnym wywołaniom funkcji I/O wykonującej zwrócenie różnych wyników ze względu na zmianę argumentów sekwencji.
I / O monada jest idiom programowania zazwyczaj wykorzystywane do wykonywania operacji we / wy w czystych językach funkcjonalnych.
Optymalizacje kompilatora
Funkcje, które mają tylko powyższą właściwość 2, umożliwiają stosowanie technik optymalizacji kompilatora, takich jak eliminacja wspólnych podwyrażeń i optymalizacja pętli, podobnie jak w przypadku operatorów arytmetycznych. Przykładem C++ jest length
metoda zwracająca rozmiar ciągu, który zależy od zawartości pamięci, na którą wskazuje ciąg, dlatego brakuje powyższej właściwości 1. Niemniej jednak w środowisku jednowątkowym następujący kod C++
std::string s = "Hello, world!";
int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int l = 0;
for (int i = 0; i < 10; ++i) {
l += s.length() + a[i];
}
można zoptymalizować w taki sposób, że wartość of s.length()
jest obliczana tylko raz, przed pętlą.
Niektóre języki programowania pozwalają na zadeklarowanie czystej własności funkcji:
- W Fortranie i D
pure
słowo kluczowe może być użyte do zadeklarowania funkcji, która jest wolna od efektów ubocznych (tj. posiada tylko powyższą właściwość 2). Kompilator może być w stanie wywnioskować właściwość 1 na górze deklaracji. - W GCC , w
pure
atrybut określa własności 2, natomiastconst
atrybut określa prawdziwie czystego funkcji z obu obiektów. - Języki oferujące wykonywanie funkcji w czasie kompilacji mogą wymagać, aby funkcje były czyste, czasami z dodatkowymi ograniczeniami. Przykłady obejmują
constexpr
C++ (obie właściwości).
Testów jednostkowych
Ponieważ czyste funkcje mają identyczne zwracane wartości dla identycznych argumentów , są dobrze przystosowane do testów jednostkowych .
Zobacz też
- Wykonanie funkcji w czasie kompilacji: ocena czystych funkcji w czasie kompilacji
- Algorytm deterministyczny
- Czysto funkcjonalna struktura danych
- Rachunek lambda
- Efekt uboczny (informatyka)
- Czysta procedura
- Idempotencja
- słowo kluczowe pure w Fortranie opisujące czyste funkcje
- słowo kluczowe constexpr w C++ opisujące czyste funkcje, które można wykorzystać w czasie kompilacji