Digamos que fn(x)
es una función pura que hace algo costoso, como devolver una lista de los factores primos de x
.
Y digamos que hacemos una versión memorable de la misma función llamada memoizedFn(x)
. Siempre devuelve el mismo resultado para una entrada dada, pero mantiene un caché privado de resultados anteriores para mejorar el rendimiento.
Hablando formalmente, ¿se memoizedFn(x)
considera puro?
¿O hay algún otro nombre o término de calificación utilizado para referirse a dicha función en las discusiones de FP? (es decir, una función con efectos secundarios que pueden afectar la complejidad computacional de las llamadas posteriores pero que pueden no afectar los valores de retorno).
terminology
pure-function
callum
fuente
fuente
funcx(){sleep(cached_time--); return 0;}
devuelve el mismo valor cada vez, pero tendrá un rendimiento diferenteRespuestas:
Si. La versión memorable de una función pura también es una función pura.
Lo único que le importa a la pureza de la función es el efecto que tienen los parámetros de entrada en el valor de retorno de la función (pasar la misma entrada siempre debe producir la misma salida) y cualquier efecto secundario relevante para los estados globales (p. Ej., Texto al terminal o UI o red) . El tiempo de cómputo y el uso de memoria adicional es irrelevante para la pureza de la función.
Las memorias caché de una función pura son prácticamente invisibles para el programa; un lenguaje de programación funcional puede optimizar automáticamente una función pura a una versión memorable de la función si puede determinar que será beneficioso hacerlo. En la práctica, determinar automáticamente cuándo la memorización es beneficiosa es en realidad un problema bastante difícil, pero dicha optimización sería válida.
fuente
Wikipedia define una "Función Pura" como una función que tiene las siguientes propiedades:
Su valor de retorno es el mismo para los mismos argumentos (sin variación con variables estáticas locales, variables no locales, argumentos de referencia mutables o flujos de entrada desde dispositivos de E / S).
Su evaluación no tiene efectos secundarios (sin mutación de variables estáticas locales, variables no locales, argumentos de referencia mutables o flujos de E / S).
En efecto, una función pura devuelve la misma salida dada la misma entrada, y no afecta nada más fuera de la función. Para fines de pureza, no importa cómo la función calcule su valor de retorno, siempre que devuelva la misma salida dada la misma entrada.
Los lenguajes funcionalmente puros como Haskell usan rutinariamente la memorización para acelerar una función almacenando en caché sus resultados previamente calculados.
fuente
static const
variable local en C ++ (pero no C), o una estructura de datos evaluada de manera perezosa en Haskell. Hay una condición más que necesita: la inicialización debe ser segura para subprocesos.Sí, las funciones puras memorizadas se conocen comúnmente como puras. Esto es especialmente común en lenguajes como Haskell, en los que los resultados inmutables, evaluados de forma perezosa y memorizados son una característica incorporada.
Hay una advertencia importante: la función de memorización debe ser segura para subprocesos, o de lo contrario podría obtener una condición de carrera cuando dos subprocesos intentan llamarla.
Un ejemplo de un científico informático que usa el término "puramente funcional" de esta manera es esta publicación de blog de Conal Elliott sobre la memorización automática:
Hay muchos ejemplos en la literatura revisada por pares, y lo han sido durante décadas. Por ejemplo, este documento de 1995, "Uso de la memorización automática como herramienta de ingeniería de software en sistemas de IA del mundo real", utiliza un lenguaje muy similar en la sección 5.2 para describir lo que hoy llamaríamos una función pura:
Algunos idiomas imperativos tienen un idioma similar. Por ejemplo, una
static const
variable en C ++ se inicializa solo una vez, antes de usar su valor, y nunca muta.fuente
Depende de cómo lo hagas.
Por lo general, la gente quiere memorizar mutando algún tipo de diccionario de caché. Esto tiene todos los problemas asociados con la mutación impura, como tener que preocuparse por la concurrencia, preocuparse por que el caché crezca demasiado, etc.
Sin embargo, puede memorizar sin mutación de memoria impura. Un ejemplo es en esta respuesta , donde hago un seguimiento externo de los valores memorizados mediante un
lengths
argumento.En el enlace que Robert Harvey proporcionó , la evaluación diferida se usa para evitar efectos secundarios.
Otra técnica que se ve a veces es marcar explícitamente la memorización como un efecto secundario impuro en el contexto de un
IO
tipo, como con la función de memorización de efecto gatos .Este último plantea el punto de que a veces el objetivo es encapsular la mutación en lugar de eliminarla. La mayoría de los programadores funcionales lo consideran "suficientemente puro" para hacer que la impureza sea explícita y encapsulada.
Si desea que un término lo diferencie de una función verdaderamente pura, creo que es suficiente decir "memorizado con un diccionario mutable". Eso les permite a las personas saber cómo usarlo de manera segura.
fuente
collatz(100)
y comocollatz(200)
cooperar. Y IIUIC, el problema con el caché que crece demasiado sigue siendo (¿aunque Haskell puede tener algunos buenos trucos para esto?).IO
es puro. Todos los métodos impurosIO
y los gatos se nombranunsafe
.Async.memoize
también es puro, por lo que no tenemos que conformarnos con "lo suficientemente puro" :)Por lo general, una función que devuelve una lista no es pura en absoluto porque requiere la asignación de almacenamiento y, por lo tanto, puede fallar (por ejemplo, al lanzar una excepción, que no es pura). Un lenguaje que tiene tipos de valor y puede representar una lista como un tipo de valor de tamaño acotado puede no tener este problema. Por esta razón, su ejemplo probablemente no sea puro.
En general, si la memorización se puede hacer de una manera libre de fallas (por ejemplo, al tener almacenamiento estáticamente asignado para resultados memorizados y una sincronización interna para controlar el acceso a ellos si el idioma admite hilos), es razonable considerar dicha función puro.
fuente
Puede implementar la memorización sin efectos secundarios utilizando la mónada estatal .
En su caso, el estado sería el valor memorizado o nada (es decir, Haskell
Maybe
o ScalaOption[A]
). Si el valor memorizado está presente, se devuelve comoA
; de lo contrario,A
se calcula y se devuelve como el estado de transición y el resultado.fuente