EDIT3: J'écris un code pour traiter une liste d'entrée très longue de Int
s avec seulement quelques centaines de non-doublons. J'utilise deux listes auxiliaires pour maintenir des sommes partielles cumulatives pour calculer une valeur d'accumulateur, le comment et le pourquoi sont sans importance. Je veux abandonner toutes les listes ici et en faire une belle boucle destructrice, et je ne sais pas comment. Je n'ai pas besoin du code entier, juste un code de squelette serait grand, étaient en lecture/écriture est fait à deux tableaux auxiliaires et un certain résultat final est retourné. Ce que j'ai en ce moment tournerait 0,5 heure pour l'entrée. J'ai codé ceci maintenant en C++, et il fonctionne en 90 secondes pour la même entrée.Comment traduire ce code basé sur une liste en utilisant des tableaux mutables?
Je ne comprends pas du tout comment faire. Voici le code basé sur une liste que j'ai en ce moment: (mais le code basé sur une carte ci-dessous est plus clair)
ins :: (Num b, Ord a) => a -> b -> [(a, b)] -> ([(a, b)], b)
ins n x [] = ([(n,x)], 0)
ins n x [email protected]((v, s):t) =
case compare n v of
LT -> ((n,s+x) : l , s)
EQ -> ((n,s+x) : t , if null t then 0 else snd (head t))
GT -> let (u,z) = ins n x t
in ((v,s+x):u,z)
Ceci est utilisé dans une boucle, pour traiter une liste de numéros de longueur connue, (changé à foldl maintenant)
scanl g (0,([],[])) ns -- ns :: [Int]
g ::
(Num t, Ord t, Ord a) =>
(t, ([(a, t)], [(a, t)])) -> a -> (t, ([(a, t)], [(a, t)]))
g (c,(a, b)) n =
let
(a2,x) = ins n 1 a
(b2,y) = if x>0 then ins n x b else (b,0)
c2 = c + y
in
(c2,(a2, b2))
Cela fonctionne, mais je dois accélérer. En C, je garderais les listes (a,b)
sous forme de tableaux; utilisez la recherche binaire pour trouver l'élément avec la clé juste au-dessus ou égale à n
(au lieu de la recherche séquentielle utilisée ici); et utilisez la mise à jour sur place pour modifier toutes les entrées précédentes.
Je ne suis vraiment intéressé par la valeur finale. Comment cela se fait-il chez Haskell, avec des tableaux mutables?
J'ai essayé quelque chose, mais je ne sais vraiment pas ce que je fais ici, et je reçois des messages d'erreur étranges et très longs (comme "impossible de déduire ... du contexte ..."):
goarr top = runSTArray $ do
let sz = 10000
a <- newArray (1,sz) (0,0) :: ST s (STArray s Int (Integer,Integer))
b <- newArray (1,sz) (0,0) :: ST s (STArray s Int (Integer,Integer))
let p1 = somefunc 2 -- somefunc :: Integer -> [(Integer, Int)]
go1 p1 2 0 top a b
go1 p1 i c top a b =
if i >= top
then
do
return c
else
go2 p1 i c top a b
go2 p1 i c top a b =
do
let p2 = somefunc (i+1) -- p2 :: [(Integer, Int)]
let n = combine p1 p2 -- n :: Int
-- update arrays and calc new c
-- like the "g" function is doing:
-- (a2,x) = ins n 1 a
-- (b2,y) = if x>0 then ins n x b else (b,0)
-- c2 = c + y
go1 p2 (i+1) c2 top a b -- a2 b2??
Cela ne fonctionne pas du tout. Je ne sais même pas comment encoder des boucles en notation. S'il vous plaît aider.
UPD: le code basée sur une carte qui fonctionne 3 fois plus lent:
ins3 :: (Ord k, Num a) => k -> a -> Map.Map k a -> (Map.Map k a, a)
ins3 n x a | Map.null a = (Map.insert n x a , 0)
ins3 n x a = let (p,q,r) = Map.splitLookup n a in
case q of
Nothing -> (Map.union (Map.map (+x) p)
(Map.insert n (x+leftmost r) r) , leftmost r)
Just s -> (Map.union (Map.map (+x) p)
(Map.insert n (x+s) r) , leftmost r)
leftmost r | Map.null r = 0
| otherwise = snd . head $ Map.toList r
UPD2: Le message d'erreur est "Impossible de déduire (Num (starray s1 ie)) du contexte() provenant du littéral `0 'à filename.hs: 417: 11"
c'est où il est dit return c
dans go1
fonction. Peut-être c
devrait être un tableau, mais je veux retourner la valeur de l'accumulateur qui est construit en utilisant les deux tableaux auxiliaires.
EDIT3: Je l'ai remplacé scanl
et (!!)
avec foldl
et take
selon les conseils de Chris, et il fonctionne maintenant dans l'espace constant avec la complexité empirique saine et est en fait devrait se terminer en moins de 0,5 heure - a.o.t. ... 3 jours! Je le savais bien sûr, mais j'étais si sûr que GHC optimise les choses, sûrement ça ne ferait pas tellement de différence, je pensais! Et donc senti que seuls les tableaux mutables pourraient aider ... Bummer.
Pourtant, C++ fait la même chose en 90 sec, et j'apprécierais beaucoup d'apprendre à coder ceci avec des tableaux mutables, dans Haskell.
Ce code est vraiment difficile à suivre. –
la deuxième moitié est la plupart du temps du charabia car je ne sais vraiment pas ce que je fais. La première moitié est un code de travail, il calcule juste quelque chose dans une boucle, en maintenant deux listes auxiliaires - que je veux transformer en tableaux, pour la vitesse. – darveter
La première moitié est encore trop difficile à suivre. Pourrions-nous avoir des signatures de type, peut-être? Ou quelques commentaires? –