Toutes les réponses existantes utilisent un horodatage qui est incrémenté à chaque opération setVal
. Ce n'est pas nécessaire. En fait, il n'est nécessaire d'incrémenter l'horodatage que sur setAll
. Un autre problème soulevé est le débordement arithmétique. Cela peut être géré sans dépasser les limites de temps constant en mettant à jour une seule cellule à chaque setAll
et en effectuant la comparaison de temps avec soin.
Comment ça fonctionne
Le concept de base est essentiellement similaire à celui des autres réponses, mais avec une particularité.
Ce qu'ils font : Stockez la valeur utilisée pour la dernière opération setAll
séparément et suivez l'heure à laquelle cette opération a été effectuée. Chaque fois qu'ils effectuent un setVal
, ils stockent l'heure actuelle avec la valeur donnée dans le tableau. Chaque fois qu'ils effectuent un getVal
, ils comparent l'heure dans l'emplacement donné avec l'heure de la dernière occurrence de setAll
, puis choisissent soit la valeur à l'emplacement, soit la valeur setAll
en fonction de laquelle est la plus grande.
Pourquoi cela peut être un problème : Supposons que l'heure actuelle déborde et qu'une opération setAll
se produise peu de temps après. Il semblera que la plupart des valeurs de tableau stockées sont plus récentes que la valeur de setAll
, alors qu'en réalité elles sont plus anciennes.
La solution : Arrêter d'imaginer que nous suivons la quantité totale de temps écoulée depuis l'initialisation de la structure de données. Imaginez une horloge géante avec une "aiguille des secondes" qui ne fait pas 60 tours autour du cercle mais plutôt 2^n tours autour du cercle. La position de l'aiguille des secondes représente l'heure de la dernière opération setAll
. Chaque opération setVal
stocke cette heure avec une valeur. Donc, si nous effectuons un setAll
lorsque l'"horloge" est à 45, puis effectuons six opérations setVal
sur différents éléments, l'heure de setAll
et les heures de tous les six emplacements seront les mêmes. Nous souhaitons maintenir l'invariant suivant :
L'heure à un emplacement donné équivaut à l'heure de setAll
si et seulement si cet élément a été réglé avec setVal
plus récemment que la dernière opération setAll
.
De toute évidence, la procédure décrite ci-dessus garantit automatiquement que si l'élément a été réglé récemment, alors son heure équivaudra à l'heure de setAll
. Le défi est de faire en sorte que l'implication inverse soit également vraie.
À suivre ....
Le code
J'ai écrit cela en Haskell car c'est le langage que je connais le mieux, mais ce n'est pas exactement le langage le plus naturel pour ce travail.
{-# LANGUAGE BangPatterns #-}
module RepArr where
import Control.Monad.Primitive
import Data.Primitive.MutVar
import qualified Data.Vector.Mutable as V
import Data.Vector.Mutable (MVector)
import Control.Applicative
import Prelude hiding (replicate)
import Control.Monad.ST
import Data.Word
-- L'Int dans le MutVar est le pointeur de rafraîchissement
data RepArr s a = RepArr (MutVar s (Word, a, Int)) (MVector s (Word,a))
-- Crée un tableau fantaisiste d'une longueur donnée, initialement rempli d'une valeur donnée
replicate :: (PrimMonad m, Applicative m) => Int -> a -> m (RepArr (PrimState m) a)
replicate n a = RepArr <$> newMutVar (0,a,0) <*> V.replicate n (0,a)
getVal :: PrimMonad m => RepArr (PrimState m) a -> Int -> m a
getVal (RepArr allv vec) n = do
(vectime, vecval) <- V.read vec n
(alltime, allval, _) <- readMutVar allv
if (fromIntegral (alltime - vectime) :: Int) > 0
then return allval
else return vecval
setVal :: PrimMonad m => RepArr (PrimState m) a -> Int -> a -> m ()
setVal (RepArr allv vec) i v = do
(!alltime, _, _) <- readMutVar allv
V.write vec i (alltime, v)
setAll :: PrimMonad m => RepArr (PrimState m) a -> a -> m ()
setAll r@(RepArr allv vec) v = do
(oldt, _, oldc) <- readMutVar allv
getVal r oldc >>= setVal r oldc
let !newc = case oldc+1 of
op1 | op1 == V.length vec -> 0
| otherwise -> op1
let !newt = oldt+1
writeMutVar allv (newt, v, newc)
Pour éviter les pauses potentielles (rarement) dues à la collecte des déchets, il est en fait nécessaire de déballer les valeurs Int
et Word
, ainsi que d'utiliser des vecteurs déballés au lieu de ceux polymorphes, mais je ne suis pas vraiment d'humeur et c'est une tâche assez mécanique.
Voici une version en C (complètement non testée) :
#include
struct Pair {
unsigned long time;
void* val;
};
struct RepArr {
unsigned long allT;
void* allV;
long count;
long length;
struct Pair vec[];
};
struct RepArr *replicate (long n, void* val) {
struct RepArr *q = malloc (sizeof (struct RepArr)+n*sizeof (struct Pair));
q->allT = 1;
q->allV = val;
q->count = 0;
q->length = n;
int i;
for (i=0; ivec[i] = foo;
}
return q;
}
void* getVal (struct RepArr *q, long i) {
if ((long)(q->vec[i].time - q->allT) < 0)
return q->allV;
else
return q->vec[i].val;
}
void setVal (struct RepArr *q, long i, void* val) {
q->vec[i].time = q->allT;
q->vec[i].val = val;
}
void setAll (struct RepArr *q, void* val) {
setVal (q, q->count, getVal (q, q->count));
q->allV = val;
q->allT++;
q->count++;
if (q->count == q->length)
q->count = 0;
}
0 votes
Est-il possible de mettre en œuvre par une table de hachage?