J'essaye de créer une fonction qui joue récursivement tous les jeux possibles de tic-tac-toe en utilisant un algorithme génétique, puis retourne un tuple de (victoires, pertes, liens). Cependant, la fonction ci-dessous la pile toujours déborde lorsqu'il est appelé comme ceci:Optimisation d'une fonction Haskell pour éviter les débordements de pile
scoreOne :: UnscoredPlayer -> [String] -> ScoredPlayer
scoreOne player boards = ScoredPlayer (token player) (chromosome player) (evaluateG $! testPlayer player boards)
...
let results = map (\x->scoreOne x boards) players
print (maximum results)
où players
une liste des chromosomes. Le débordement ne se produit pas avec seulement 1 joueur, mais avec deux cela arrive.
EDIT: Si la fonction est appelée de la manière suivante, elle ne déborde pas de la pile.
let results = map (\player -> evaluateG (testPlayer player boards)) players
print (maximum results)
Cependant, la façon suivante fait débordement de la pile.
let results = map (\player -> ScoredPlayer (token player) (chromosome player) (evaluateG $! testPlayer player boards)) players
Pour référence, ScoredPlayer
est défini comme (la chaîne est le jeton de joueur, [Int] est le chromosome, et le flotteur est le score):
D'après ce que je sais de Haskell, la fonction playAll'
n'est pas récursive en arrière car l'appel foldl'
que j'utilise effectue un traitement supplémentaire sur les résultats de la fonction. Cependant, je n'ai aucune idée de comment éliminer l'appel foldl'
, car il est nécessaire de s'assurer que tous les jeux possibles sont joués. Est-il possible de restructurer la fonction pour qu'elle soit récursive à la queue (ou au moins ne déborde pas la pile)?
Merci d'avance, et désolé pour la liste de code massive.
playAll' :: (Num a) => UnscoredPlayer -> Bool -> String -> [String] -> (a,a,a) -> (a,a,a)
playAll' player playerTurn board boards (w,ls,t)=
if won == self then (w+1,ls,t) -- I won this game
else
if won == enemy then (w,ls+1,t) -- My enemy won this game
else
if '_' `notElem` board then (w,ls,t+1) -- It's a tie
else
if playerTurn then --My turn; make a move and try all possible combinations for the enemy
playAll' player False (makeMove ...) boards (w,ls,t)
else --Try each possible move against myself
(foldl' (\(x,y,z) (s1,s2,s3) -> (x+s1,y+s2,z+s3)) (w,ls,t)
[playAll' player True newBoard boards (w,ls,t)| newBoard <- (permute enemy board)])
where
won = winning board --if someone has one, who is it?
enemy = (opposite.token) player --what player is my enemy?
self = token player --what player am I?
Au lieu d'un triple paresseux' (a, a, a) ', essayez un enregistrement personnalisé (plus stricte) type. Par exemple. 'données T = T! Int! Int!Int' –
Je l'ai implémenté comme 'data R = R! Float! Float! Float' mais ça donne quand même un débordement de pile pour deux joueurs, désolé. –
Ok, j'ai trouvé la solution (ou au moins un hack): Au lieu de définir 'UnscoredPlayer', j'ai juste utilisé les tuples (UnscoredPlayer, Float). Cela fonctionne même avec 100 joueurs. –