C'est un bon exercice pour devenir plus fluide dans ce langage de programmation que vous vouliez apprendre, mais que vous n'avez que légèrement modifié. Cela implique de travailler avec des objets, d'utiliser ou de simuler des fermetures et d'étirer le système de texte.
Votre tâche consiste à écrire du code pour gérer les listes paresseuses, puis à l'utiliser pour implémenter cet algorithme pour générer des nombres de Fibonacci:
Les exemples de code sont en Haskell
let fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
in take 40 fibs
Résultat:
[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]
L'implémentation de votre liste paresseuse doit respecter ces directives:
- Un nœud de liste est l'une des trois choses:
- Nil - Liste vide.
[]
- Inconvénients - Un seul élément, associé à une liste des éléments restants:
1 : [2,3,4,5]
(:
est l'opérateur des inconvénients à Haskell) - Thunk - Un calcul différé qui produit un noeud List en cas de besoin.
- Nil - Liste vide.
- Il prend en charge les opérations suivantes:
- nil - Construit une liste vide.
- cons - Construit une cellule contre.
- thunk - Construit un Thunk, étant donné une fonction qui ne prend aucun argument et renvoie un Nil ou un Cons.
- force - Étant donné un nœud de liste:
- Si c'est un néant ou un inconvénient, renvoyez-le simplement.
- S'il s'agit d'un Thunk, appelez sa fonction pour obtenir un Nil ou un Cons. Remplacez le thunk par ce Nil ou Contre, et retournez-le.
Remarque: le remplacement du thunk par sa valeur forcée est une partie importante de la définition de "paresseux" . Si cette étape est ignorée, l'algorithme de Fibonacci ci-dessus sera trop lent.
- empty - Voir si un noeud List est Nil (après l'avoir forcé).
- head (aka "car") - Obtenez le premier élément d'une liste (ou lancez un ajustement si c'est nul).
- tail (aka "cdr") - Obtenez les éléments après la tête d'une liste (ou lancez un ajustement si c'est Nil).
- zipWith - Étant donné une fonction binaire (par exemple
(+)
) et deux listes (éventuellement infinies), appliquez la fonction aux éléments correspondants des listes. Exemple:
zipWith (+) [1,2,3] [1,1,10] == [2,3,13]
- take - Étant donné un nombre N et une liste (éventuellement infinie), prenez les N premiers éléments de la liste.
- print - Imprime tous les éléments d'une liste. Cela devrait fonctionner de manière incrémentielle lorsque vous disposez d'une liste longue ou infinie.
fibs
s'utilise dans sa propre définition. La mise en place d'une récursivité paresseuse est un peu délicate; vous devrez faire quelque chose comme ceci:- Attribuer un thunk pour
fibs
. Laissez-le dans un état factice pour l'instant. - Définissez la fonction thunk, qui dépend d'une référence à
fibs
. - Mettez à jour le thunk avec sa fonction.
Vous souhaiterez peut-être masquer cette plomberie en définissant une fonction
fix
qui appelle une fonction de retour de liste avec sa propre valeur de retour. Pensez à faire une petite sieste pour que cette idée puisse s'installer.- Attribuer un thunk pour
Le polymorphisme (la capacité de travailler avec des listes de tout type d'élément) n'est pas requis, mais voyez si vous pouvez trouver un moyen de le faire qui soit idiomatique dans votre langue.
- Ne vous inquiétez pas de la gestion de la mémoire. Même les langages avec garbage collection ont tendance à transporter des objets que vous n'utiliserez plus jamais (par exemple sur la pile d'appels), alors ne soyez pas surpris si votre programme perd de la mémoire en parcourant une liste infinie.
N'hésitez pas à dévier légèrement de ces lignes directrices pour tenir compte des particularités de votre langue, ou pour explorer des approches alternatives aux listes paresseuses.
Règles:
- Choisissez une langue que vous ne connaissez pas bien. Je ne peux pas "exiger" cela, d'où la balise "honor-system". Cependant, les électeurs peuvent vérifier votre historique pour voir dans quelles langues vous avez posté.
N'utilisez pas le support de liste paresseuse intégré de votre langue pour tout faire. Postez quelque chose de substantiel ou du moins intéressant.
Haskell est à peu près sorti. Autrement dit, à moins que vous ne fassiez quelque chose comme ceci:
data List a = IORef (ListNode a) data ListNode a = Nil | Cons !a !(List a) | Thunk !(IO (ListNode a))
Remarque: l'évaluation non stricte de Haskell n'est pas interdite, mais l'implémentation de votre liste paresseuse ne doit pas en tirer directement la capacité. En fait, il serait intéressant de voir une solution efficace et purement fonctionnelle qui ne nécessite pas de paresse.
Python:
- N'utilisez pas itertools.
- Les générateurs vont bien, mais vous les utilisez, vous devrez trouver un moyen de mémoriser les valeurs forcées.
zipWith (+) [1,2,3,4,5] [0,0,0] == [1,2,3]
. Cependant, cela n'a pas d'importance pour l'algorithme de Fibonacci ci-dessus, car les deux arguments de zipWith sont des listes infinies.
fibs
correctement, car cela dépend de lui-même. J'ai mis à jour la question pour élaborer sur la récursivité paresseuse. FUZxxl l'a compris lui-même.
zipWith
sur deux listes de longueurs différentes?