web-dev-qa-db-fra.com

Trouver l'index d'un élément dans une liste dans Haskell?

J'ai une fonction dans Haskell qui trouve la valeur maximale d'une exponentiation dans une liste:

prob99 = maximum $ map (\xs -> (head xs)^(head (tail xs))) numbers

Ce que je dois trouver, c'est l'emplacement de cette valeur maximale dans la liste résultante. Comment pourrais-je m'y prendre?

Edit: j'ai trouvé une solution qui va comme ceci:

n = [[519432,525806],[632382,518061]....
prob99b [a,b] = b* (log a)
answer = snd $ maximum (Zip  (map prob99b n) [1..])
28
Jonno_FTW

Comment trouver l'indice de l'élément maximum? Que diriez-vous d'essayer tous les index et de vérifier s'ils sont au maximum?

ghci> let maxIndex xs = head $ filter ((== maximum xs) . (xs !!)) [0..]

Mais cela ressemble à quelque chose pour lequel une fonction existe déjà. Mon code sera plus lisible, maintenable et probablement encore plus efficace si j'utilisais la fonction existante.

Pour info, vous pouvez également demander Hoogle qui peut rechercher par les signatures de type Haskell (comme Will l'a suggéré):

$ hoogle "Ord a => [a] -> Int" | head

<Nothing relevant>

$ # hmm, so no function to give me the index of maximum outright,
$ # but how about finding a specific element, and I give it the maximum?
$ hoogle "a -> [a] -> Int" | head
Data.List elemIndex :: Eq a => a -> [a] -> Maybe Int
Data.List elemIndices :: Eq a => a -> [a] -> [Int]
35
yairchu
import Data.List
elemIndex 'b' "abc" === Just 1

Un très bon outil pour trouver des fonctions haskell est Hoogle . Vous permet entre autres de rechercher par signature de type.

Si vous vouliez tout faire en une seule fois, je recommanderais Data.List.mapAccumL, en passant l'index du plus grand nombre trouvé jusqu'à l'accumulateur.

33
Will

Cela ne mérite probablement pas d'être dans sa propre réponse, mais je ne peux pas encore commenter. Quoi qu'il en soit, voici comment j'aurais écrit ceci:

import Data.List
import Data.Ord

maxIndex ::  Ord a => [a] -> Int
maxIndex = fst . maximumBy (comparing snd) . Zip [0..]
7
Maxime Henrion

Si vous effectuez un calcul numérique dans Haskell, vous voudrez peut-être examiner les bibliothèques qui le rendent plus facile et plus efficace. Par exemple hmatrix a une méthode maxIndex pour efficace Vectors, dont la documentation est ici: https://hackage.haskell.org/package /hmatrix-0.17.0.1/docs/Numeric-LinearAlgebra-Data.html#g:14

> maxIndex $ vector [1, 3, 2]
1

Les noms exacts des méthodes étaient différents lorsque la question a été posée à l'origine, mais la bibliothèque existait également à l'époque.

1
Daniel Landau

En tant que novice Haskeller que je suis, je penserais dans ce sens:

  1. Les listes Haskell sont des listes liées, nous devons donc attacher manuellement les index aux éléments: Zip [0..] xs. (Notez que l'indexation Haskell commence à zéro, c'est-à-dire que la tête d'une liste est x !! 0.)
  2. Je veux trouver le maximum de cette liste zippée [(0, x!!0), (1, x!!1), ..., (n, x!!n)] en fonction du deuxième élément de chaque Tuple. Il existe quelques alternatives, basées sur ma connaissance de Haskell:

    • Je pourrais écrire ceci comme Maxime écrit ci-dessous , en utilisant maximumBy (comparing snd), si je savais seulement que ces fonctions existent.
    • Je ne savais pas que maximumBy existe mais je soupçonnais quelque chose comme ça, donc je pouvais le rechercher sur Hoogle en fonction de la signature de type que j'utiliserais: il retourne un élément de type type générique a à partir d'une liste d'éléments a, en utilisant une fonction de comparaison sur deux paramètres a (a -> a -> Ordering) - au total, ce serait [a] -> (a -> a -> Ordering) -> a, ou une permutation plus logique des arguments. En y pensant, mettre la liste comme avant-dernier argument a plus de sens, car cela permet un meilleur curry comme nous le verrons dans un instant, alors recherchons(a -> a -> Ordering) -> [a] -> a.
    • Je ne sais rien ou je pense que je peux tout écrire moi-même (ou je veux juste), alors je peux écrire ceci comme:
    import Data.List (foldl1')
    
    maxBySnd :: Ord a => [(Int, a)] -> (Int, a)
    maxBySnd = foldl1 cmpBySnd
      where
        cmpBySnd (i, xi) (j, xj) = case xi `compare` xj of
                                     LT -> (j, xj)
                                     _  -> (i, xi)
    

    foldl1' Commence à se plier à partir de la gauche (ce qui signifie que l'accumulateur est à gauche dans la fonction de pliage), et l'index accumulé est mis à jour uniquement si xj est supérieur à xi, donc cela renverra l'index du premier maximum. Si quelqu'un a une rancune contre l'importation de Data.List Et ne fonctionne pas avec des listes de 1000 éléments, alors foldl1 De Prelude fera l'affaire. Le package Data.Vector Utilise une approche similaire, il suffit de rechercher "maxIndex" ici et ici .

    • Je ne connais même pas la classe de type Ord pour des objets comparables, auquel cas cmpBySnd deviendrait
        cmpBySnd (i, xi) (j, xj) = if xi < xj then j else i
    

    À ce stade, on manquerait les avantages des types de données algébriques et des fonctions d'ordre supérieur (ce qui est presque impossible à réaliser si l'on ne sait pas), donc 1) c'est bien que vous posiez la question! 2) puis-je diriger le lecteur suivant vers une ressource comme Learn You A Haskell For Great Good , or Real World Haskell , or this SO answer , ou ce dépôt GitHub .

  3. Nous devons encore prendre le premier élément du tuple (i, xi) Résultant, de préférence avec la fonction intégrée fst :: (a, b) -> a, ou avec la fonction maison first (a,b) = a

Le résultat final est le suivant:

import Data.List (maximumBy)
import Data.Ord (comparing)

maxIndex :: Ord a => [a] -> Int
maxIndex = fst . maximumBy (comparing snd) . Zip [0..]
-- or
-- maxIndex = fst . maxBySnd . Zip [0..]
1
Laszlo Treszkai