En me basant sur l'excellente réponse de jbapple concernant replicate, mais en utilisant replicateA(qui replicateest construit sur) à la place, j'ai trouvé ce qui suit:
--Unlike fromList, one needs the length explicitly.
myFromList :: Int -> [b] -> Seq b
myFromList l xs = flip evalState xs $ Seq.replicateA l go
where go = do
(y:ys) <- get
put ys
return y
myFromList(dans une version plus légère efficace) est déjà défini et utilisé en interne dans Data.Sequencepour la construction d' arbres de doigts qui sont les résultats de toutes sortes.
En général, l'intuition de replicateAest simple. replicateAest construit au-dessus de la fonction applicativeTree . applicativeTreeprend un morceau d'arbre d'une taille met produit un arbre bien équilibré contenant des ncopies de celui-ci. Les cas pour njusqu'à 8 (un seul Deepdoigt) sont codés en dur. Tout ce qui est au-dessus de cela, et il s’invoque récursivement. L'élément "applicatif" est simplement qu'il entrelace la construction de l'arbre avec des effets de filetage à travers, comme, dans le cas du code ci-dessus, l'état.
La gofonction, qui est répliquée, est simplement une action qui obtient l'état actuel, fait apparaître un élément par le haut et remplace le reste. A chaque appel, il descend ainsi plus loin dans la liste fournie en entrée.
Quelques notes plus concrètes
main = print (length (show (Seq.fromList [1..10000000::Int])))
Sur certains tests simples, cela a donné un compromis intéressant sur les performances. La fonction principale ci-dessus a fonctionné presque 1/3 de moins avec myFromList qu'avec fromList. D'autre part, myFromListutilisé un tas constant de 2 Mo, tandis que la norme fromListutilisait jusqu'à 926 Mo. Ce 926 Mo découle de la nécessité de conserver la liste entière en mémoire à la fois. Pendant ce temps, la solution avec myFromListest capable de consommer la structure en mode streaming paresseux. Le problème de la vitesse résulte du fait qu'il myFromListdoit effectuer environ deux fois plus d'allocations (en raison de la construction / destruction de la paire de la monade d'État) quefromList. Nous pouvons éliminer ces allocations en passant à une monade d'état transformée par CPS, mais cela aboutit à conserver beaucoup plus de mémoire à un moment donné, car la perte de paresse nécessite de parcourir la liste de manière non continue.
D'un autre côté, si plutôt que de forcer toute la séquence avec un spectacle, je passe à l'extraction de la tête ou du dernier élément, myFromListprésente immédiatement un gain plus important - l'extraction de l'élément de tête est presque instantanée, et l'extraction du dernier élément est de 0,8 s . Pendant ce temps, avec la norme fromList, l'extraction de la tête ou du dernier élément coûte environ 2,3 secondes.
Ce ne sont que des détails et une conséquence de la pureté et de la paresse. Dans une situation de mutation et d'accès aléatoire, j'imagine que la replicatesolution est strictement meilleure.
Cependant, cela soulève la question de savoir s'il existe un moyen de réécrire applicativeTreece qui myFromListest strictement plus efficace. Le problème est, je pense, que les actions applicatives sont exécutées dans un ordre différent de celui de l'arbre qui est naturellement traversé, mais je n'ai pas complètement expliqué comment cela fonctionne, ou s'il existe un moyen de résoudre ce problème.