En programación informática , una función pura es una función que tiene las siguientes propiedades: [1] [2]
Los siguientes ejemplos de funciones de C++ son puros:
floor
, devolviendo el piso de un numero;max
, devolviendo el máximo de dos valores.void f () { static std :: atómico < int sin signo > x = 0 ; ++ x ; }
x
solo se puede observar dentro de otras invocaciones de f()
, y como f()
no comunica el valor de x
a su entorno, es indistinguible de una función void f() {}
que no hace nada. Tenga en cuenta que esto x
es std::atomic
para que las modificaciones de varios subprocesos que se ejecutan f()
simultáneamente no generen una carrera de datos , que tiene un comportamiento indefinido en C y C++.Las siguientes funciones de C++ son impuras porque carecen de la propiedad 1 mencionada anteriormente:
int f () { int estático x = 0 ; ++ x ; devolver x ; }
int f () { devolver x ; }
sin()
no es pura, ya que su resultado depende del modo de redondeo IEEE , que se puede cambiar en tiempo de ejecución.int f ( int * x ) { devolver * x ; }
int f () { int x = 0 ; std :: cin >> x ; devolver x ; }
Las siguientes funciones de C++ son impuras porque carecen de la propiedad 2 mencionada anteriormente:
void f () { int estático x = 0 ; ++ x ; }
vacío f () { ++ x ; }
vacío f ( int * x ) { ++* x ; }
void f () { std :: cout << "¡Hola, mundo!" << std :: endl ; }
Las siguientes funciones de C++ son impuras ya que carecen de las propiedades 1 y 2 mencionadas anteriormente:
int f () { int estático x = 0 ; ++ x ; devolver x ; }
int f () { int x = 0 ; std :: cin >> x ; devolver x ; }
La E/S es inherentemente impura: las operaciones de entrada socavan la transparencia referencial y las operaciones de salida crean efectos secundarios. Sin embargo, hay un sentido en el que una función puede realizar operaciones de entrada o salida y aún así ser pura, si la secuencia de operaciones en los dispositivos de E/S relevantes se modela explícitamente como argumento y como resultado, y se considera que las operaciones de E/S fallan cuando la secuencia de entrada no describe las operaciones realmente realizadas desde que el programa comenzó a ejecutarse. [ aclaración necesaria ]
El segundo punto garantiza que la única secuencia utilizable como argumento debe cambiar con cada acción de E/S; el primero permite que diferentes llamadas a una función que realiza E/S devuelvan resultados diferentes debido a que los argumentos de la secuencia han cambiado. [3] [4]
La mónada E/S es un modismo de programación que normalmente se utiliza para realizar E/S en lenguajes puramente funcionales.
Los resultados de una función pura se pueden calcular previamente y almacenar en caché en una tabla de búsqueda. En una técnica denominada memorización , cualquier resultado que se devuelva de una función determinada se almacena en caché y la próxima vez que se llame a la función con los mismos parámetros de entrada, se devuelve el resultado almacenado en caché en lugar de volver a calcular la función.
La memorización se puede realizar envolviendo la función en otra función ( función envolvente ). [5]
Mediante la memorización se puede reducir el esfuerzo computacional involucrado en los cálculos de la propia función, a costa de la sobrecarga de gestión de la caché y de un aumento de los requerimientos de memoria.
Un programa en C para el cálculo en caché de factoriales ( assert()
se interrumpe con un mensaje de error si su argumento es falso; en una máquina de 32 bits, los valores superiores fact(12)
no se pueden representar de todos modos. [ cita requerida ]
static int fact ( int n ) { return n <= 1 ? 1 : fact ( n -1 ) * n ; } int fact_wrapper ( int n ) { static int caché [ 13 ]; assert ( 0 <= n && n < 13 ); if ( caché [ n ] == 0 ) caché [ n ] = fact ( n ); return caché [ n ]; }
Las funciones que tienen solo la propiedad 2 anterior (es decir, no tienen efectos secundarios) permiten técnicas de optimización del compilador, como la eliminación de subexpresiones comunes y la optimización de bucles , de manera similar a los operadores aritméticos. [6] Un ejemplo de C++ es el length
método que devuelve el tamaño de una cadena, que depende del contenido de la memoria a la que apunta la cadena, por lo que carece de la propiedad 1 anterior. Sin embargo, en un entorno de un solo subproceso , el siguiente código C++
std :: string s = "¡Hola, mundo!" ; int a [ 10 ] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 }; int l = 0 ; para ( int i = 0 ; i < 10 ; ++ i ) { l += s . length () + a [ i ]; }
se puede optimizar de modo que el valor de s.length()
se calcule solo una vez, antes del bucle.
Algunos lenguajes de programación permiten declarar una propiedad pura a una función:
pure
palabra clave se puede utilizar para declarar que una función está libre de efectos secundarios (es decir, que solo tiene la propiedad 2 mencionada anteriormente). [7] El compilador puede deducir la propiedad 1 sobre la declaración. [8] Véase también: Características del lenguaje Fortran 95 § Procedimientos puros .pure
atributo especifica la propiedad 2, mientras que el const
atributo especifica una función verdaderamente pura con ambas propiedades. [9]constexpr
C++ (ambas propiedades). [10] Véase también: C++11 § constexpr – Expresiones constantes generalizadas .Dado que las funciones puras tienen valores de retorno idénticos para argumentos idénticos , son adecuadas para pruebas unitarias .