¿Alguien sabe cuál es la peor desaceleración asintótica posible que puede ocurrir cuando se programa puramente funcionalmente en lugar de imperativo (es decir, permitiendo efectos secundarios)?
Aclaración del comentario de itowlson : ¿hay algún problema para el cual el algoritmo no destructivo más conocido sea asintóticamente peor que el algoritmo destructivo más conocido y, en caso afirmativo, por cuánto?
Respuestas:
Según Pippenger [1996] , al comparar un sistema Lisp que es puramente funcional (y tiene una semántica de evaluación estricta, no perezosa) con uno que puede mutar datos, se puede traducir un algoritmo escrito para el Lisp impuro que se ejecuta en O ( n ) a un algoritmo en el Lisp puro que se ejecuta en tiempo O ( n log n ) (basado en el trabajo de Ben-Amram y Galil [1992] sobre la simulación de memoria de acceso aleatorio utilizando solo punteros). Pippenger también establece que hay algoritmos para los que es lo mejor que puede hacer; hay problemas que son O ( n ) en el sistema impuro que son Ω ( n log n ) en el sistema puro.
Hay algunas advertencias sobre este documento. Lo más significativo es que no aborda los lenguajes funcionales perezosos, como Haskell. Bird, Jones y De Moor [1997] demuestran que el problema construido por Pippenger se puede resolver en un lenguaje funcional vago en O ( n ) tiempo, pero no establecen (y que yo sepa, nadie tiene) si No un lenguaje funcional perezoso puede resolver todos los problemas en el mismo tiempo de ejecución asintótico que un lenguaje con mutación.
El problema construido por Pippenger requiere que Ω ( n log n ) esté específicamente construido para lograr este resultado, y no es necesariamente representativo de problemas prácticos del mundo real. Existen algunas restricciones sobre el problema que son un poco inesperadas, pero necesarias para que la prueba funcione; en particular, el problema requiere que los resultados se calculen en línea, sin poder acceder a entradas futuras, y que la entrada consista en una secuencia de átomos de un conjunto ilimitado de átomos posibles, en lugar de un conjunto de tamaño fijo. Y el documento solo establece resultados (límite inferior) para un algoritmo impuro de tiempo de ejecución lineal; para problemas que requieren un mayor tiempo de ejecución, es posible que el O adicional (log n) factor visto en el problema lineal puede ser "absorbido" en el proceso de operaciones adicionales necesarias para algoritmos con tiempos de ejecución mayores. Estas aclaraciones y preguntas abiertas son exploradas brevemente por Ben-Amram [1996] .
En la práctica, se pueden implementar muchos algoritmos en un lenguaje funcional puro con la misma eficiencia que en un lenguaje con estructuras de datos mutables. Para obtener una buena referencia sobre las técnicas que se utilizarán para implementar estructuras de datos puramente funcionales de manera eficiente, consulte "Estructuras de datos puramente funcionales" de Chris Okasaki [Okasaki 1998] (que es una versión ampliada de su tesis [Okasaki 1996] ).
Cualquiera que necesite implementar algoritmos en estructuras de datos puramente funcionales debería leer Okasaki. Siempre puede obtener, en el peor de los casos, una desaceleración de O (log n ) por operación simulando memoria mutable con un árbol binario equilibrado, pero en muchos casos puede hacerlo considerablemente mejor que eso, y Okasaki describe muchas técnicas útiles, desde técnicas amortizadas hasta técnicas reales. tiempo que hacen el trabajo amortizado de forma incremental. Las estructuras de datos puramente funcionales pueden ser un poco difíciles de trabajar y analizar, pero proporcionan muchos beneficios, como la transparencia referencial, que son útiles en la optimización del compilador, en la computación paralela y distribuida, y en la implementación de características como el control de versiones, deshacer y deshacer.
Tenga en cuenta también que todo esto solo analiza los tiempos de ejecución asintóticos. Muchas técnicas para implementar estructuras de datos puramente funcionales le brindan una cierta cantidad de desaceleración constante del factor, debido a la contabilidad adicional necesaria para que funcionen, y detalles de implementación del idioma en cuestión. Los beneficios de las estructuras de datos puramente funcionales pueden ser mayores que estas desaceleraciones constantes de los factores, por lo que generalmente necesitará hacer compensaciones en función del problema en cuestión.
Referencias
fuente
De hecho, hay varios algoritmos y estructuras de datos para los que no se conoce una solución puramente funcional asintóticamente eficiente (una implementable en cálculo lambda puro), incluso con pereza.
Sin embargo, suponemos que en lenguajes "imperativos" el acceso a la memoria es O (1), mientras que en teoría eso no puede ser tan asintótico (es decir, para tamaños de problemas ilimitados) y el acceso a la memoria dentro de un gran conjunto de datos es siempre O (log n) , que se puede emular en un lenguaje funcional.
Además, debemos recordar que en realidad todos los lenguajes funcionales modernos proporcionan datos mutables, y Haskell incluso los proporciona sin sacrificar la pureza (la mónada ST).
fuente
Este artículo afirma que las implementaciones puramente funcionales conocidas del algoritmo de búsqueda de unión tienen una complejidad asintótica peor que la que publican, que tiene una interfaz puramente funcional pero utiliza datos mutables internamente.
El hecho de que otras respuestas afirmen que nunca puede haber ninguna diferencia y que, por ejemplo, el único "inconveniente" del código puramente funcional es que puede ser paralelo, le da una idea de la información / objetividad de la comunidad de programación funcional sobre estos asuntos .
EDITAR:
Los comentarios a continuación señalan que una discusión sesgada de los pros y los contras de la programación funcional pura puede no provenir de la "comunidad de programación funcional". Buen punto. Quizás los defensores que veo son, por citar un comentario, "analfabetos".
Por ejemplo, creo que esta publicación de blog está escrita por alguien que podría decirse que es representativo de la comunidad de programación funcional, y dado que es una lista de "puntos para una evaluación perezosa", sería un buen lugar para mencionar cualquier inconveniente que programación perezosa y puramente funcional podría tener. Un buen lugar habría estado en lugar del siguiente despido (técnicamente cierto, pero sesgado hasta el punto de no ser gracioso):
fuente
Con un límite superior fijo en el uso de memoria, no debería haber diferencia.
Boceto de prueba: dado un límite superior fijo en el uso de la memoria, uno debería poder escribir una máquina virtual que ejecute un conjunto de instrucciones imperativas con la misma complejidad asintótica que si estuviera ejecutando en esa máquina. Esto es así porque puede administrar la memoria mutable como una estructura de datos persistente, dando O (log (n)) lectura y escritura, pero con un límite superior fijo en el uso de la memoria, puede tener una cantidad fija de memoria, lo que hace que estos decaimiento a O (1). Por lo tanto, la implementación funcional puede ser la versión imperativa que se ejecuta en la implementación funcional de la VM, por lo que ambos deben tener la misma complejidad asintótica.
fuente
Sugeriría leer sobre el rendimiento de Haskell , y luego echar un vistazo a los rendimientos del juego de referencia para los lenguajes funcionales frente a los de procedimiento / OO.
fuente