在Haskell中查找列表中的元素索引?

时间:2009-09-30 09:18:16

标签: list haskell indexing

我在Haskell中有一个函数,它从列表中找到取幂的最大值:

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

我需要找到的是结果列表中此最大值的位置。我该怎么做呢?

编辑:我找到了一个类似的解决方案:

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

5 个答案:

答案 0 :(得分:34)

如何找到最大元素的索引?如何尝试所有索引并检查它们是否是最大值?

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

但这听起来像功能已经存在的东西。如果我使用现有的函数,我的代码将更具可读性,可维护性,甚至可能更高效。

所以我应该问问怎么做,在15分钟内我会得到一个答案和一些讽刺的评论。或者 - 我可以问Hoogle并立即得到有用的回应(如同建议的那样)

$ 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]

答案 1 :(得分:33)

import Data.List
elemIndex 'b' "abc" === Just 1

查找haskell函数的一个非常好的工具是Hoogle。允许您通过类型签名等搜索。

如果您想在一次传递中完成所有操作,我建议使用Data.List.mapAccumL,将迄今为止找到的最大数字的索引作为累加器传递。

答案 2 :(得分:7)

这可能不值得回答他自己,但我还不能发表评论。无论如何,这就是我写这个的方式:

import Data.List
import Data.Ord

maxIndex ::  Ord a => [a] -> Int
maxIndex = fst . maximumBy (comparing snd) . zip [0..]

答案 3 :(得分:1)

如果您在Haskell中进行数值计算,您可能需要查看使其更容易和更有效的库。例如,hmatrix有一个有效maxIndex的方法Vector,其文档位于:https://hackage.haskell.org/package/hmatrix-0.17.0.1/docs/Numeric-LinearAlgebra-Data.html#g:14

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

当问题最初被问到时,方法的确切名称是不同的,但库也在那时。

答案 4 :(得分:1)

作为我的Haskeller新手,我会按照以下思路考虑:

  1. Haskell列表是链接列表,因此我们需要将索引手动附加到元素:zip [0..] xs。 (请注意,Haskell索引从零开始,即列表的头是x !! 0。)
  2. 我想根据每个元组的第二个元素找到此压缩列表[(0, x!!0), (1, x!!1), ..., (n, x!!n)]的最大值。根据我对Haskell的了解程度,有几种选择:

    • 如果我只知道这些功能存在,我可以使用maximumBy (comparing snd)将其写为Maxime wrote below
    • 我不知道maximumBy存在,但是我怀疑是这样,所以我可以根据要使用的类型签名在Hoogle上进行搜索:它返回一个类型的元素在两个a参数(a)上使用比较函数,从a元素列表中选择通用类型a -> a -> Ordering –要么是[a] -> (a -> a -> Ordering) -> a,要么是参数的一些逻辑排列。考虑一下,将列表作为倒数第二个参数更有意义,因为这样一来,我们就会看到更好的循环,因此let's search for (a -> a -> Ordering) -> [a] -> a
    • 我一无所知,或者我想自己可以(或只是想)写所有东西,所以我可以这样写:

    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'从左侧开始折叠(这意味着折叠功能中的累加器在左侧),并且仅当xj大于xi时,累积索引才会更新,因此这将返回第一个最大值的索引。如果有人对导入Data.List怀恨在心,并且无法处理1000个项目长的列表,那么Prelude中的foldl1就可以了。 Data.Vector包使用类似的方法,只搜索“ maxIndexherehere

    • 我什至不知道类似对象的类型类Ord,在这种情况下cmpBySnd会变成

        cmpBySnd (i, xi) (j, xj) = if xi < xj then j else i
    

    在这一点上,人们可能会错过代数数据类型和高阶函数的好处(如果不知道,这几乎是不可能实现的,所以1)问的很好! 2)我可以将下一个读者指向诸如Learn You A Haskell For Great GoodReal World Haskellthis SO answerthis GitHub repo之类的资源。

  3. 我们仍然需要获取结果(i, xi)元组的第一个元素,最好使用内置函数fst :: (a, b) -> a或自制函数first (a,b) = a

最终结果如下:

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