He notado que la mayoría de los lenguajes funcionales emplean una lista enlazada individualmente (una lista de "contras") como sus tipos de lista más fundamentales. Los ejemplos incluyen Common Lisp, Haskell y F #. Esto es diferente a los idiomas principales, donde los tipos de listas nativas son matrices.
¿Porqué es eso?
Para Common Lisp (que se escribe dinámicamente) tengo la idea de que las desventajas son lo suficientemente generales como para ser también la base de listas, árboles, etc. Esto podría ser una pequeña razón.
Sin embargo, para los idiomas tipados estáticamente, no puedo encontrar un buen razonamiento, incluso puedo encontrar contraargumentos:
- El estilo funcional fomenta la inmutabilidad, por lo que la facilidad de inserción de la lista vinculada es menos ventajosa,
- El estilo funcional fomenta la inmutabilidad, también el intercambio de datos; una matriz es más fácil de compartir "parcialmente" que una lista vinculada,
- También podría hacer una coincidencia de patrones en una matriz regular, e incluso mejor (podría doblar fácilmente de derecha a izquierda, por ejemplo),
- Además de eso, obtienes acceso aleatorio gratis,
- Y (una ventaja práctica) si el idioma se escribe estáticamente, puede emplear un diseño de memoria regular y obtener un aumento de velocidad de la memoria caché.
Entonces, ¿por qué preferir las listas vinculadas?
an array is easier to share "partially" than a linked list
necesario aclarar lo que quieres decir. Debido a su naturaleza recursiva, lo contrario es cierto según lo entiendo: puede compartir parcialmente una lista vinculada más fácilmente pasando cualquier nodo en ella, mientras que una matriz necesitaría pasar tiempo haciendo una nueva copia. O en términos de intercambio de datos, dos listas vinculadas pueden apuntar al mismo sufijo, lo que simplemente no es posible con las matrices.Respuestas:
El factor más importante es que puede anteponer una lista inmutable enlazada individualmente en tiempo O (1), lo que le permite construir recursivamente listas de elementos n en tiempo O (n) de esta manera:
Si hiciera esto usando matrices inmutables, el tiempo de ejecución sería cuadrático porque cada
cons
operación necesitaría copiar toda la matriz, lo que llevaría a un tiempo de ejecución cuadrático.Supongo que al compartir "parcialmente" quiere decir que puede tomar una submatriz de una matriz en el tiempo O (1), mientras que con las listas vinculadas solo puede tomar la cola en el tiempo O (1) y todo lo demás necesita O (n). Eso es verdad.
Sin embargo, tomar la cola es suficiente en muchos casos. Y debe tener en cuenta que ser capaz de crear subarrays a bajo costo no le ayuda si no tiene forma de crearlos a bajo costo. Y (sin optimizaciones inteligentes del compilador) no hay forma de construir una matriz de forma económica paso a paso.
fuente
Creo que todo se reduce a que las listas se implementan con bastante facilidad en el código funcional.
Esquema:
Haskell
Las matrices son más difíciles y no tan bonitas de implementar. Si quieres que sean extremadamente rápidos, entonces deberán escribirse a bajo nivel.
Además, la recursividad funciona mucho mejor en listas que en matrices. Considere la cantidad de veces que ha consumido / generado recursivamente una lista frente a una matriz indexada.
fuente
Una lista enlazada individualmente es la estructura de datos persistente más simple .
Las estructuras de datos persistentes son esenciales para una programación puramente funcional y funcional.
fuente
Puede usar los nodos Cons fácilmente solo si tiene un lenguaje de recolección de basura.
Los nodos Contras coinciden mucho con el estilo de programación funcional de llamadas recursivas y valores inmutables. Por lo tanto, encaja bien en el modelo de programador mental.
Y no olvides razones históricas. ¿Por qué todavía se llaman Nodos Cons y peor aún usan car y cdr como accesores? La gente lo aprende de los libros de texto y cursos y luego lo usa.
Tiene razón, en el mundo real, las matrices son mucho más fáciles de usar, consumen solo la mitad del espacio de memoria y tienen mucho rendimiento debido a errores de nivel de caché. No hay razón para usarlos con idiomas imperativos.
fuente
Las listas vinculadas son importantes por la siguiente razón:
Una vez que tome un número como 3, y lo convierta en secuencia sucesora como
succ(succ(succ(zero)))
, y luego use la sustitución con{succ=List node with some memory space}
, y{zero = end of list}
, terminará con una lista vinculada (de longitud 3).La parte importante real es números, sustitución y espacio de memoria y cero.
fuente