编辑:我一定不能说清楚,但我正在寻找一个之类的函数,但不完全正确。
给定一个列表,我希望能够找到列表中最大元素的索引
(所以,list !! (indexOfMaximum list) == maximum list
)
我写了一些看似非常高效的代码,虽然我觉得我正在重新发明轮子。
indexOfMaximum :: (Ord n, Num n) => [n] -> Int
indexOfMaximum list =
let indexOfMaximum' :: (Ord n, Num n) => [n] -> Int -> n -> Int -> Int
indexOfMaximum' list' currIndex highestVal highestIndex
| null list' = highestIndex
| (head list') > highestVal =
indexOfMaximum' (tail list') (1 + currIndex) (head list') currIndex
| otherwise =
indexOfMaximum' (tail list') (1 + currIndex) highestVal highestIndex
in indexOfMaximum' list 0 0 0
现在我想返回列表中最大n个数字的索引列表。
我唯一的解决方案是将前n个元素存储在列表中,并将(head list') > highestVal
替换为n个最大的列表中的比较。
感觉必须有一种比这更有效的方式,我也觉得我没有充分利用Prelude和Data.List。有什么建议?
答案 0 :(得分:5)
最短路径找到最大元素的最后一个索引
maxIndex list = snd . maximum $ zip list [0 .. ]
如果你想要第一个索引,
maxIndex list = snd . maximumBy cmp $ zip list [0 .. ]
where
cmp (v,i) (w,j) = case compare v w of
EQ -> compare j i
ne -> ne
缺点是maximum
和maximumBy
太懒了,所以这些可能会构建大量的thunk。为了避免这种情况,要么使用手动递归(就像你做的那样,但是可能需要一些严格的注释)或者使用严格的左侧折叠和严格的累加器类型,元组不是很好,因为{{ 1}}只评估弱头普通形式,即这里最外层的构造函数,因此你在元组组件中构建thunk。
答案 1 :(得分:5)
此解决方案将每个元素与其索引相关联,对列表进行排序,因此最小元素是第一个,将其反转,使最大元素为第一个,获取前n个元素,然后提取索引。
maxn n xs = map snd . take n . reverse . sort $ zip xs [0..]
答案 2 :(得分:2)
嗯,一种简单的方法是使用maximum
查找最大的元素,然后使用findIndices
查找每个元素。类似的东西:
largestIndices :: Ord a => [a] -> [Int]
largestIndices ls = findIndices (== maximum ls) ls
然而,这并不完美,因为maximum
是一个部分功能,并且如果给出一个空列表,将会非常可靠。您可以通过添加[]
案例轻松避免这种情况:
largestIndices :: Ord a => [a] -> [Int]
largestIndices [] = []
largestIndices ls = findIndices (== maximum ls) ls
这个答案的真正诀窍是我如何弄明白。我之前甚至都不知道findIndices
!但是,GHCi有一个称为:browse
的简洁命令。
Prelude> :browse Data.List
列出Data.List
导出的每个函数。使用此功能,我只需先搜索maximum
,然后搜索index
以查看选项。而且,在findIndex
之后,有findIndecies
,这是完美的。
最后,除非您确实看到代码运行缓慢,否则我不会担心效率。 GHC可以 - 并且确实 - 执行一些非常积极的优化,因为语言是纯粹的,它可以逃脱它。因此,您需要担心性能的唯一时间是 - 在使用-O2编译后 - 您会发现这是一个问题。
编辑:如果你想找到n
顶级元素'索引,这里有一个简单的想法:按降序对列表进行排序,获取第一个n
个唯一元素,用{{获取它们的索引1}}并从中获取第一个elemIndices
索引。我希望这个比较清楚。
以下是我的想法的快速版本:
n