Este es un buen ejercicio para ser más fluido en ese lenguaje de programación que has querido aprender, pero que solo has jugado ligeramente. Esto implica trabajar con objetos, usar o simular cierres y estirar el sistema de tipos.
Su tarea es escribir código para administrar listas perezosas, luego usarlo para implementar este algoritmo para generar números de Fibonacci:
Los ejemplos de código están en Haskell
let fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
in take 40 fibs
Resultado:
[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269,2178309,3524578,5702887,9227465,14930352,24157817,39088169,63245986]
La implementación de su lista perezosa debe cumplir con estas pautas:
- Un nodo Lista es una de tres cosas:
- Nil: lista vacía.
[]
- Contras: un solo elemento, junto con una Lista de los elementos restantes:
1 : [2,3,4,5]
(:
es el operador de contras en Haskell) - Thunk: un cálculo diferido que produce un nodo Lista cuando es necesario.
- Nil: lista vacía.
- Es compatible con las siguientes operaciones:
- nil: construye una lista vacía.
- contras - Construye una celda contras.
- thunk: construye un Thunk, dada una función que no toma argumentos y devuelve Nil o Cons.
- force - Dado un nodo Lista:
- Si es Nil o Contras, simplemente devuélvalo.
- Si es un Thunk, llame a su función para obtener un Nil o Contras. Reemplace el golpe con ese Nil o Contras, y devuélvalo.
Nota: Reemplazar el thunk con su valor forzado es una parte importante de la definición de "perezoso" . Si se omite este paso, el algoritmo de Fibonacci anterior será demasiado lento.
- vacío: comprueba si un nodo Lista es Nulo (después de forzarlo).
- head (también conocido como "auto"): obtén el primer elemento de una lista (o haz un ataque si es Nil).
- tail (también conocido como "cdr"): obtenga los elementos después del encabezado de una lista (o haga un ajuste si es Nil).
- zipWith: dada una función binaria (p
(+)
. ej. ) y dos listas (posiblemente infinitas), aplique la función a los elementos correspondientes de las listas. Ejemplo:
zipWith (+) [1,2,3] [1,1,10] == [2,3,13]
- take: dado un número N y una lista (posiblemente infinita), tome los primeros N elementos de la lista.
- print: imprime todos los elementos de una lista. Esto debería funcionar de forma incremental cuando se le da una lista larga o infinita.
fibs
se utiliza en su propia definición. Configurar la recursividad perezosa es un poco complicado; deberás hacer algo como esto:- Asignar un golpe para
fibs
. Déjalo en un estado ficticio por ahora. - Defina la función thunk, que depende de una referencia a
fibs
. - Actualiza el thunk con su función.
Es posible que desee ocultar esta tubería definiendo una función
fix
que llame a una función de retorno de Lista con su propio valor de retorno. Considera tomar una siesta corta para que esta idea pueda surgir.- Asignar un golpe para
No se requiere el polimorfismo (la capacidad de trabajar con listas de cualquier tipo de elemento), pero vea si puede encontrar una manera de hacerlo que sea idiomática en su idioma.
- No te preocupes por la gestión de la memoria. Incluso los idiomas con recolección de basura tienden a transportar objetos que nunca volverá a usar (por ejemplo, en la pila de llamadas), así que no se sorprenda si su programa pierde memoria mientras recorre una lista infinita.
Siéntase libre de desviarse ligeramente de estas pautas para acomodar los detalles de su idioma, o explorar enfoques alternativos para listas perezosas.
Reglas:
- Elige un idioma que no conozcas bien. No puedo "exigir" esto, de ahí la etiqueta "sistema de honor". Sin embargo, los votantes pueden revisar su historial para ver en qué idiomas ha estado publicando.
No use el soporte integrado de la lista diferida de su idioma para hacer todo. Publicar algo sustancial o al menos interesante.
Haskell está prácticamente fuera. Es decir, a menos que haga algo como esto:
data List a = IORef (ListNode a) data ListNode a = Nil | Cons !a !(List a) | Thunk !(IO (ListNode a))
Nota: La evaluación no estricta de Haskell no está prohibida, pero la implementación de su lista perezosa no debería derivar su capacidad directamente de allí. De hecho, sería interesante ver una solución eficiente y puramente funcional que no requiera pereza.
Pitón:
- No utilices itertools.
- Los generadores están bien, pero si los usa, tendrá que encontrar alguna forma de memorizar valores forzados.
zipWith (+) [1,2,3,4,5] [0,0,0] == [1,2,3]
. Sin embargo, esto no importa para el algoritmo de Fibonacci anterior, ya que ambos argumentos para zipWith son listas infinitas.
fibs
correctamente, ya que depende de sí mismo. Actualicé la pregunta para elaborar sobre la recursión perezosa. FUZxxl lo descubrió por sí mismo.
zipWith
dos listas de diferentes longitudes?