- tleb,
Bonjour,
Ce sujet est pour obtenir un retour sur mon code, voir si vous avez des conseils d’amélioration / des critiques. J’apprécie la philosophie fonctionnel mais je ne m’y suis jamais vraiment mis à fond donc je m’essaie à résoudre des problèmes avec Haskell.
Voici une traduction du problème : étant donné une liste d’entiers, retourner une nouvelle liste tel que chaque élément à l’indice i
de la nouvelle liste soit le produit de tous les nombres de la liste originale sauf celui à l’indice i
. Autrement dit, solve([1 2 3 4 5]) == [120 60 40 30 24]
. Sous forme d’algorithme impératif, si ma description n’était pas très clair :
def solve(ls):
res = []
for i, _ in enumerate(ls):
tmp = 1
for j, m in enumerate(ls):
if i != j:
tmp *= m
res.append(tmp)
return res
En commençant, je me dis que la solution est probablement récursive. Voici donc ma première version qui est pas mal tiré par les cheveux :
solve :: [Int] -> Maybe Int -> [Int]
solve [] _ = []
solve (x:xs) (Just product) = [div product x] ++ (solve xs (Just product))
solve ls@(x:xs) Nothing = [div product x] ++ (solve xs (Just product))
where product = foldr (*) 1 ls
En fait, map
est là pour ça :
solve :: [Int] -> [Int]
solve ls = map (div product) ls
where product = foldr (*) 1 ls
Edit, currying <3 :
solve :: [Int] -> [Int]
solve = map (div product)
where product = foldr (*) 1 ls
Je suis plutôt satisfait de cette version, j’ai du mal à voir comment on peut faire mieux. On commence d’ailleurs à découvrir la puissance du langage avec les syntaxes (div product)
et (*)
par exemple. Cependant, le problème vient avec une contrainte optionnelle : que faire si on ne peut pas utiliser la division ?
solve :: [Int] -> [Int]
solve [] = []
solve ls = map (\(i, _) -> foldr (\(j, n) acc -> if i == j then acc else acc*n) 1 zipped) zipped
where zipped = zip [0..] ls
J’utilise quelques petits trucs apportés par Haskell genre l’évaluation fainéante ([0..]
) mais je trouve que la solution n’est toujours pas idéale. Pour chaque valeur, on fold
toutes les valeurs sauf celle à l’indice actuel ; c’est un peu une traduction mot à mot de l’énoncé. La solution n’est pas très lisible je trouve. Vous avez des conseils ?
J’ai eu du mal à me lancer mais maintenant que j’arrive relativement à écrire du Haskell, j’ai juste envie de continuer à l’explorer et à me casser la tête pour trouver des solutions élégantes. J’espère que ce sujet pourra continuer à vivre pour parler Haskell, et beau code.