我需要让列表的元素满足谓词和这些元素的索引。我可以这样做:
import Data.List (findIndices)
list :: [Int]
list = [3,2,4,1,9]
indices = findIndices (>2) list
elems = [list!!i | i <- indices]
-- same as: elems = filter (>2) list
是不是有一个包提供了一个功能,在“一次性”中给出了元素及其索引?我很惊讶我在某处找不到这个功能。否则,如何做这样的功能,改进我上面的代码?我不相信这段代码是最优的,因为它以某种方式访问列表的元素两次。我快速浏览了findIndices
的源代码,但我还不明白。
答案 0 :(得分:7)
您可以通过过滤(索引,元素)元组列表来提高效率 - 避免!!
访问。
let (indices, elems) = unzip [(i, x) | (i, x) <- zip [0..] list, x > 2]
分成适当的功能:
findItems :: (a -> Bool) -> [a] -> [(Int, a)]
findItems predicate = filter (predicate . snd) . zip [0..]
let (indices, elems) = unzip $ findItems (>2) list
可能有一种更直接的方式,我很乐意找到它:)
答案 1 :(得分:6)
我认为Ry's suggestion很好。对于更直接的,特别是更通用的,您可以使用lens
tooling:
Prelude> import Control.Lens as L
Prelude L> import Control.Arrow as A
Prelude L A> ifoldr (\i x -> if x>2 then (i:)***(x:) else id) ([],[]) [3,2,4,1,9]
([0,2,4],[3,4,9])
这也可以立即用于数组(索引提取更有用)
Prelude L A> import qualified Data.Vector as V
Prelude L A V> ifoldr (\i x -> if x>2 then (i:)***(x:) else id) ([],[]) $ V.fromList [3,2,4,1,9]
([0,2,4],[3,4,9])
...即使是未装箱的,也不是Foldable
:
Prelude L A V> import qualified Data.Vector.Unboxed as VU
Prelude L A V VU> import Data.Vector.Generic.Lens as V
ifoldrOf vectorTraverse (\i x -> if x>2 then (i:)***(x:) else id) ([],[]) $ VU.fromList [3,2,4,1,9]
([0,2,4],[3.0,4.0,9.0])
答案 2 :(得分:3)
(indices, elems) = unzip [ item | item <- zip [0..] ls, (snd item) > 2 ]
不确定它是否更高效,但它可以“一次性”完成。