Czysta funkcja - Pure function

W programowaniu komputerowym , wykorzystując czystą funkcją jest funkcja , która ma następujące właściwości:

  1. W funkcji wartości powrotnetakie same dla tych samych argumentów (brak zróżnicowania lokalnych zmiennych statycznych , zmiennych nielokalnych , modyfikowalnych argumentów odniesienia lub strumieni wejściowych ).
  2. 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
    void f() {
      static std::atomic<unsigned int> x = 0;
      ++x;
    }
    
    Chociaż ten przykładowy kod wygląda, jakby nie był czysty, w rzeczywistości tak jest. Wartość of xmożna zaobserwować tylko wewnątrz innych wywołań f(), a ponieważ f()nie przekazuje wartości xswojemu otoczeniu, jest nie do odróżnienia od funkcji void f() {}, która nic nie robi. Należy pamiętać, że xjest std::atomictak, że modyfikacje z wielu wątków wykonujących f()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ą
    int f() {
      return x;
    }
    
    Z tego samego powodu np. funkcja biblioteczna C++ 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 lengthmetoda 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 puresł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 pureatrybut określa własności 2, natomiast constatrybut 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ą constexprC++ (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ż

Bibliografia