La bibliothèque standard Haskell a-t-elle une fonction qui, étant donné une liste et un prédicat, renvoie le nombre d'éléments satisfaisant ce prédicat? Quelque chose comme avec le type (a -> Bool) -> [a] -> Int
. Ma recherche sur Google n'a retourné rien d'intéressant. J'utilise actuellement length . filter pred
, que je ne trouve pas être une solution particulièrement élégante. Mon cas d'utilisation semble être assez courant pour avoir une meilleure solution de bibliothèque que cela. Est-ce le cas ou ma prémonition est-elle fausse?
Le length . filter p
l'implémentation n'est pas aussi mauvaise que vous le suggérez. En particulier, il n'a que des frais généraux constants en mémoire et en vitesse, alors oui.
Pour les choses qui utilisent la fusion de flux, comme le package vector
, length . filter p
sera en fait optimisé pour éviter de créer un vecteur intermédiaire. Les listes, cependant, utilisent ce qu'on appelle foldr/build
fusion pour le moment, ce qui n'est pas assez intelligent pour optimiser length . filter p
sans créer de gros morceaux linéaires risquant de déborder.
Pour plus de détails sur la fusion de flux, voir cet article . Si je comprends bien, la raison pour laquelle la fusion de flux n'est pas actuellement utilisée dans les principales bibliothèques Haskell est que (comme décrit dans l'article) environ 5% des programmes ont des performances dramatiques pires lorsqu'il est implémenté au-dessus des bibliothèques basées sur les flux, tandis que foldr/build
les optimisations ne peuvent jamais (AFAIK) aggraver activement les performances.
Non, il n'y a pas de fonction prédéfinie qui le fasse, mais je dirais que length . filter pred
est, en fait, une implémentation élégante; c'est aussi proche que possible d'exprimer ce que vous voulez dire sans invoquer directement le concept, ce que vous ne pouvez pas faire si vous le définissez.
Les seules alternatives seraient une fonction récursive ou un pli, ce que l'OMI serait moins élégant, mais si vous voulez vraiment:
foo :: (a -> Bool) -> [a] -> Int
foo p = foldl' (\n x -> if p x then n+1 else n) 0
Il s'agit simplement d'inclure length
dans la définition. Quant à la dénomination, je suggérerais count
(ou peut-être countBy
, puisque count
est un nom de variable raisonnable).
Haskell est un langage de haut niveau. Plutôt que de fournir une fonction pour chaque combinaison possible de circonstances que vous pourriez rencontrer, il vous fournit un petit ensemble de fonctions qui couvrent toutes les bases, puis vous les collez ensemble selon les besoins pour résoudre le problème actuel.
En termes de simplicité et de concision, c'est aussi élégant que possible. Donc oui, length . filter pred
est absolument la solution standard. Comme autre exemple, considérons elem
, qui (comme vous le savez peut-être) vous indique si un élément donné est présent dans une liste. L'implémentation de référence standard pour cela est en fait
elem :: Eq x => x -> [x] -> Bool elem x = foldr (||) Faux. carte (x ==)
Dans les mots d'ordre, comparez chaque élément de la liste à l'élément cible, créant une nouvelle liste de Bools. Pliez ensuite la fonction OR logique sur cette nouvelle liste.
Si cela semble inefficace, essayez de ne pas vous en soucier. En particulier,
Le compilateur peut souvent optimiser les structures de données temporaires créées par un code comme celui-ci. (N'oubliez pas, c'est la manière standard d'écrire du code dans Haskell, donc le compilateur est réglé pour y faire face.)
Même s'il ne peut pas être optimisé, la paresse rend souvent ce code assez efficace de toute façon.
(Dans cet exemple spécifique, la fonction OR terminera la boucle dès qu'une correspondance est vue - tout comme ce qui se passerait si vous la codiez vous-même à la main.)
En règle générale, écrivez du code en collant des fonctions préexistantes. Ne modifiez cela que si les performances ne sont pas suffisantes.
Ceci est ma solution amateur à un problème similaire. Compter le nombre d'entiers négatifs dans une liste l
nOfNeg l = length(filter (<0) l)
main = print(nOfNeg [0,-1,-2,1,2,3,4] ) --2
En 2020, il n'y a en effet aucun idiome de ce type dans la bibliothèque standard de Haskell! On pourrait (et devrait) cependant insérer un idiome howMany
(ressemblant au bon vieux any
)
howMany p xs = sum [ 1 | x <- xs, p x ]
-- howMany=(length.).filter
main = print $ howMany (/=0) [0..9]