Hoy estaba leyendo sobre la función pura, me confundí con su uso:
Se dice que una función es pura si devuelve el mismo conjunto de valores para el mismo conjunto de entradas y no tiene efectos secundarios observables.
por ejemplo, strlen()
es una función pura mientras que rand()
es impura.
__attribute__ ((pure)) int fun(int i)
{
return i*i;
}
int main()
{
int i=10;
printf("%d",fun(i));//outputs 100
return 0;
}
El programa anterior se comporta de la misma manera que en ausencia de pure
declaración.
¿Cuáles son los beneficios de declarar una función como pure
[si no hay cambios en la salida]?
c
pure-virtual
Duende Verde
fuente
fuente
printf
; por ejemplo, calificaría (llamarla dos veces con los mismos argumentos arroja el mismo valor de retorno), pero no es pura....and no side-effects...
papel.Respuestas:
pure
le permite al compilador saber que puede hacer ciertas optimizaciones sobre la función: imagina un poco de código comofor (int i = 0; i < 1000; i++) { printf("%d", fun(10)); }
Con una función pura, el compilador puede saber que necesita evaluar
fun(10)
una vez y solo una vez, en lugar de 1000 veces. Para una función compleja, es una gran victoria.fuente
strlen
. Entonces otra vez. Lo mismo, ¿sí? Ahora modifique el segundo carácter para que sea\0
. ¿strlen
Todavía devuelve 1000 ahora? La dirección inicial es la misma (== la entrada es la misma) pero la función ahora devuelve un valor diferente.strlen
(en GCC / glibc) es de hecho puro. Pero una mirada a la implementación de glibc mostró que esto estaba mal.Cuando dice que una función es 'pura', está garantizando que no tiene efectos secundarios visibles externamente (y como dice un comentario, si miente, pueden suceder cosas malas). Saber que una función es 'pura' tiene beneficios para el compilador, que puede usar este conocimiento para realizar ciertas optimizaciones.
Esto es lo que dice la documentación de GCC sobre el
pure
atributo:La respuesta de Philip ya muestra cómo saber que una función es 'pura' puede ayudar con las optimizaciones de bucle.
Aquí hay uno para la eliminación de subexpresión común (dado
foo
es puro):a = foo (99) * x + y; b = foo (99) * x + z;
Puede llegar a ser:
_tmp = foo (99) * x; a = _tmp + y; b = _tmp + z;
fuente
call
instrucción es un cuello de botella para las CPU superescalares, la ayuda del compilador puede ayudar.foo
es parte de otra unidad de compilación (otro archivo C), o en una biblioteca precompilada. En ambos casos, el compilador no sabrá lo quefoo
hace y no puede pre-calcular.Además de los posibles beneficios en tiempo de ejecución, es mucho más fácil razonar sobre una función pura cuando se lee el código. Además, es mucho más fácil probar una función pura, ya que sabe que el valor de retorno solo depende de los valores de los parámetros.
fuente
Una función no pura
int foo(int x, int y) // possible side-effects
es como una extensión de una función pura
int bar(int x, int y) // guaranteed no side-effects
en el que tiene, además de los argumentos de función explícita x, y, el resto del universo (o cualquier cosa con la que su computadora pueda comunicarse) como una entrada potencial implícita. Del mismo modo, además del valor de retorno entero explícito, cualquier cosa que su computadora pueda escribir es implícitamente parte del valor de retorno.
Debe quedar claro por qué es mucho más fácil razonar sobre una función pura que sobre una no pura.
fuente
Solo como complemento, me gustaría mencionar que C ++ 11 codifica las cosas de alguna manera usando la palabra clave constexpr. Ejemplo:
#include <iostream> #include <cstring> constexpr unsigned static_strlen(const char * str, unsigned offset = 0) { return (*str == '\0') ? offset : static_strlen(str + 1, offset + 1); } constexpr const char * str = "asdfjkl;"; constexpr unsigned len = static_strlen(str); //MUST be evaluated at compile time //so, for example, this: int arr[len]; is legal, as len is a constant. int main() { std::cout << len << std::endl << std::strlen(str) << std::endl; return 0; }
Las restricciones en el uso de constexpr hacen que la función sea demostrablemente pura. De esta manera, el compilador puede optimizar de manera más agresiva (¡solo asegúrese de usar la recursividad de cola, por favor!) Y evaluar la función en tiempo de compilación en lugar de en tiempo de ejecución.
Entonces, para responder a su pregunta, es que si está usando C ++ (sé que dijo C, pero están relacionados), escribir una función pura en el estilo correcto le permite al compilador hacer todo tipo de cosas interesantes con la función: -)
fuente
En general, las funciones puras tienen 3 ventajas sobre las funciones impuras que el compilador puede aprovechar:
Almacenamiento en caché
Digamos que tiene una función pura
f
que se llama 100000 veces, ya que es determinista y depende solo de sus parámetros, el compilador puede calcular su valor una vez y usarlo cuando sea necesarioParalelismo
Las funciones puras no leen ni escriben en ninguna memoria compartida y, por lo tanto, pueden ejecutarse en subprocesos separados sin consecuencias inesperadas.
Pasando por referencia
Una función
f(struct t)
obtiene su argumentot
por valor y, por otro lado, el compilador puede pasart
por referencia af
si se declara como pura mientras garantiza que el valor det
no cambiará y tendrá ganancias de rendimiento.Además de las consideraciones sobre el tiempo de compilación, las funciones puras se pueden probar con bastante facilidad: simplemente llámelas.
No es necesario construir objetos o simular conexiones a bases de datos / sistema de archivos.
fuente