25

Estoy incursionando en lenguajes funcionales y he encontrado algunos algoritmos (especialmente aquellos que usan programación dinámica) más difíciles de escribir y, a veces, menos eficientes en el peor de los casos. ¿Existe una clase de algoritmos que son menos eficientes en los lenguajes funcionales con variables inmutables y, por lo tanto, efectos secundarios?¿Qué algoritmos son difíciles de implementar en los lenguajes funcionales?

¿Y hay una referencia que alguien pueda indicarme que ayude con los algoritmos más difíciles de escribir (quizás aquellos optimizados por estado compartido)?

Gracias

+2

http: // stackoverflow.com/questions/1990464/efficiency-of-pure-functional-programming –

+1

Si bien es relativamente frecuente la pérdida de rendimiento al intentar utilizar el mismo * algoritmo * en un lenguaje funcional frente a uno imperativo, ese es el caso menos frecuente cuando considere diferentes algoritmos que resuelvan el mismo problema del mundo real. Si eres un programador imperativo con experiencia y estás incursionando en lenguajes funcionales, entonces toda tu experiencia pensando en algoritmos estará sesgada hacia aquellos adecuados en un contexto imperativo. – Ben

Respuesta

32

En primer lugar, ya que puede o no puede estar al tanto, algunos idiomas, como Haskell, implementar el intercambio, lo que alivia algunos de los problemas que podría imaginar.

Si bien la respuesta de Andrew apunta a la completitud de Turing, en realidad no responde a la pregunta de qué algoritmos son hard para implementar en lenguajes funcionales. En lugar de preguntar qué son los algoritmos difíciles de implementar en los lenguajes funcionales, la gente suele preguntar qué estructuras de datos son difíciles de implementar en los lenguajes funcionales.

La respuesta simple a esto: cosas que involucran punteros.

No hay un equivalente funcional a los punteros cuando se profundiza en el nivel de la máquina, hay un mapa y puede compilar ciertas estructuras de datos de forma segura en arreglos o cosas implementadas como punteros, pero en un nivel alto, puede Exprese cosas usando estructuras de datos basadas en punteros tan fácilmente como sea posible en idiomas imperativos.

Para evitar esto, una serie de cosas se han hecho:

  • Desde punteros son la base de una tabla hash, y puesto que las tablas hash realmente sólo implementan un mapa, mapas funcionales eficientes han sido estudiados exhaustivamente . De hecho, Chris Okasaki tiene un libro ("Estructuras de datos puramente funcionales") que detalla muchas, muchas formas de implementar mapas funcionales, deques, etc.
  • Dado que los punteros se pueden usar para representar nodos dentro del recorrido de algunos más grandes estructura de datos, también ha habido trabajo en esta área. El producto es cremallera, una estructura eficiente que representa sucintamente el equivalente funcional de la técnica "puntero dentro de una estructura más profunda".
  • Dado que los punteros se pueden utilizar para implementar cálculos de efectos secundarios, mónadas se han utilizado para expresar este tipo de cálculos de una manera bonita. Como es difícil hacer un seguimiento del estado, un uso para las mónadas es permitirle a máscara comportarse como parte de su programa y usar el sistema de tipos para asegurarse de que el programa esté encadenado correctamente (mediante enlaces monádicos) .

Si bien yo había como decir que cualquier algoritmo puede ser traducido a partir de uno imprescindible para una funcional muy fácilmente, esto simplemente no es el caso. Sin embargo, estoy bastante convencido de que el problema no son los algoritmos per se, sino las estructuras de datos que manipulan, que se basan en una noción imperativa de estado.Puede encontrar una larga lista de estructuras de datos funcionales en this post.

La otra cara de todo esto es que si comienza a utilizar un estilo más puramente funcional, gran parte de la complejidad de su programa disminuye, y muchas necesidades de mucho las estructuras de datos imperativas desaparecen (por ejemplo, un uso muy común de punteros en lenguajes imperativos es implementar patrones desagradables de diseño, que generalmente se traducen en usos inteligentes del polimorfismo y las clases de tipo a nivel funcional).

EDIT: Creo que la esencia de esta pregunta se refiere a cómo expresar el cálculo de una manera funcional. Sin embargo, debe tenerse en cuenta que hay formas de definir el cálculo con estado de una manera funcional. O más bien, es posible usar técnicas funcionales para razonar acerca del cálculo con estado. Por ejemplo, el proyecto Ynot hace esto usando una mónada parametrizada donde los hechos sobre el montón (en forma de lógica de separación) son rastreados por los enlaces monádicos.

Por cierto, incluso en ML, no veo por qué la programación dinámica es que difícil. Parece que los problemas de programación dinámica, que generalmente crean colecciones de alguna secuencia para calcular una respuesta final, pueden acumular los valores construidos mediante argumentos a la función, lo que tal vez requiera una continuación en algunas circunstancias. Usando la repetición de la cola no hay razón para que esto no sea tan bonito y eficiente como en los lenguajes imperativos. Ahora, seguro, puede encontrarse con el argumento de que si estos valores son listas (por ejemplo), podemos implementarlos de forma imperativa como matrices, pero para eso, consulte el contenido de la publicación propiamente dicha :-)

+2

He hecho una gran cantidad de algoritmos de traducción en lenguajes funcionales. Probablemente el único que nunca he logrado ver hecho satisfactoriamente es el algoritmo de sufijo-trie de Ukkonen, pero ese es un trabajo muy sofisticado en cualquier caso. –

7

Recuerde que la mayoría los lenguajes funcionales permiten cierta noción de efectos secundarios; pueden ser mal vistos, restringidos al uso local, etc., pero aún puede usarlos. En OCaml, Haskell, F #, Scala o Clojure, puede usar matrices mutables si lo necesita.

Así que si encuentra un algoritmo para el cual tiene una formulación que utiliza matrices mutables, y necesita reproducirlo en uno de estos lenguajes, ¡simplemente use matrices mutables!

No hay ninguna razón para obligarse a hacer todo con un solo paradigma de programación; Hay algunos dominios de problemas donde la programación imperativa es (dado nuestro conocimiento actual) la herramienta más adecuada para el trabajo, del mismo modo que hay dominios donde la programación lógica es excelente. Si le ahorra tiempo y esfuerzo realizar un uso local y encapsulado de uno de estos paradigmas, no dude en utilizarlos.

Por ejemplo, el Sieve of Eratosthenes es trivial de implementar con matrices mutables, y mucho más difícil de imitar (con una eficiencia razonable) de una manera puramente funcional: ver Melissa O'Neill artículo para más detalles.

Por otro lado, encontrar soluciones inmutables a un problema determinado puede ser un ejercicio interesante y esclarecedor. El libro "Estructuras de datos puramente funcionales" de Crhis Okasaki es un buen ejemplo de reformulaciones muy agradables de algoritmos de una manera puramente funcional. Si está interesado en el algoritmo por sí mismo (en lugar de su aplicación a su problema), esta puede ser una actividad muy interesante.

(Para ejemplos de usos de intercambio para optimizar un algoritmo puramente funcional, véase Richard Bird y Ralf Hinze de 2003 Functional Pearl: Trouble Shared is Trouble Halved.)

+0

Estoy completamente de acuerdo con esta respuesta, y en _práctica_ esto es probablemente lo que sucede la mayor parte del tiempo, (aunque los Haskellers obviamente parecen mucho más puramente funcionalmente orientados) –

2

Uno puede implementar características imperativas con un bajo coste asintótico, por lo que en un sentido abstracto no existe dificultad esencial para traducir el código imperativo al universo puramente funcional. En la práctica, por supuesto, existe. :-) Eche un vistazo a "Pure versus Impure Lisp" de Pippenger y papers that cite it.

Cuestiones relacionadas