7
votes

Structures de données purement fonctionnelles avec copie-ware?

Je veux avoir l'avantage des structures de données fonctionnelles (plusieurs versions de données pouvant partager la structure) mais pouvoir la modifier dans un style impératif.

Qu'est-ce que je pense (et une utilisation éventuelle): un jeu RPG dans lequel l'historique du jeu entier est stocké (par exemple, pour permettre de retourner dans le temps). En utilisant Copy-On-Write, je pourrais simplement cloner la structure de la tenue de l'état du jeu et la modifier en introduisant un nouveau tour - mais avoir accès aux virages précédents (pas nécessairement tous, peut-être simplement sélectionné des instantanés d'état de jeu), sans le pénalité de devoir tout copier à chaque fois. p>


Disons FOO code> est une carte. p>

baz.modifyAgain()


1 commentaires

probablement pyrsistent est que cherchez-vous


3 Réponses :


8
votes

Les structures de données fonctionnelles ("persistantes") sont généralement reconstruits de manière récursive de l'immutable Les nœuds (par exemple, la liste liée liée où des suffixes courants sont partagés, une arborescence de recherche ou un tas où seules les parties de la structure de l'arborescence sur le chemin de la racine à l'élément mis à jour doivent avoir besoin de copier).

Quelque chose où l'ensemble de l'ensemble doit être copié pour chaque modification est mauvais. Dans ces cas, vous avez tendance à superposer de petits "diffs" qui sont vérifiés (prenant du temps supplémentaire) avec une récursion aux versions précédentes. Chaque fois, lorsque le diffs est trop grand, vous pouvez faire une copie / reconstruction profonde (le coût amorti n'est donc pas aussi mauvais).

Les structures de données persistantes peuvent avoir des frais généraux significatifs, mais si vous avez une allocation très efficace de petits objets (JVM GC qualifie), ils peuvent être très pratiques - dans le meilleur cas, aussi vite que l'équivalent mutable, donnant une persistance uniquement à Le coût de la mémoire utilisée - qui peut être beaucoup moins qu'une copie complète sur chaque mise à jour.

Selon votre langue, vous trouverez probablement la syntaxe que vous souhaitez que vous souhaitiez de réaliser sans surcharge de l'opérateur pour la tâche. Lvalues ​​(références mutables) en C ++ nécessite définitivement une sémantique non persistante.


0 commentaires

0
votes

Ce sonne bien assoluré et souillé par erreur par rapport à une structure de données entièrement immuable, puis à l'aide d'une enveloppe qui contient une référence à celle-ci et expose une interface impérative qui fonctionne en mettant à jour la version enveloppée.

E.g. Dans SCALA P>

class ImmutableData{
   def doStuff(blah : Blah) : ImmutableData = implementation
}

class MutableData(var wrapped : ImmutableData){
  def doStuff(blah : Blah) : Unit = { wrapped = wrapped.doStuff(blah); } 
}


2 commentaires

À droite, mais cela signifie mettre à jour tout le reste manuellement - je ne peux pas utiliser de telle mutableata dans une autre structure de données immuable.


Je ne suis pas suivi. Si vous souhaitez l'utiliser de manière imturable, vous pouvez obtenir une version d'instantanée en le débarrassant.



0
votes

1. Cela a-t-il été fait et dans quelle mesure?

Oui, voir par exemple qt5 Partage implicite .

2. Est-ce une bonne idée? Sinon, y a-t-il un moyen de l'enregistrer?

Oui, c'est une bonne idée. L'une des solutions de remplacement proposées est d'utiliser une structure de données entièrement immuable (enveloppée dans une interface impérative), mais le problème est que même si un objet est le seul à pointer vers une donnée, une opération de modification créera une copie des données. (Il n'y a pas de mise à jour en place), cela est inefficace. Utilisation de la copie sur l'approche d'écriture, une copie est faite uniquement dans la première modification, modifications suivantes modifie les données copiées en place (si une autre référence aux mêmes données n'a pas été créée, hors du cours).

3. Comment pourrait-il être mis en œuvre? Je pensais à la construire sur une langue gc-ed de haut niveau, comme Python.

Un moyen est d'utiliser le comptage de référence, comme dans qt (voir une description ici ). Pour mettre en œuvre, il nécessitera une surcharge de l'opérateur d'attribution ou une méthode explicite (comme bar = foo.clone () , mais il peut être fragile, que se passe-t-il si quelqu'un oublie d'appeler clone et juste faire bar = foo ?), le comptage peut donc être conservé.

Autre possibilité de créer un objet proxy, comme vous l'avez dit. Voir par exemple un PYCOW (une implémentation de python).


0 commentaires