我将用一个例子来解释我的问题,因为我不确定把它写成文字的最好方法。
假设我有两个列表a
和b
:
a = ["car", "bike", "train"]
和b = [1, 3]
我想通过选择位置与c
中的整数相对应的a
中的项目来创建新列表b
,因此列出c = ["car", "train"]
我如何在Haskell中执行此操作?我想我必须使用列表理解但不确定如何。干杯。
答案 0 :(得分:3)
直接方法是使用(!!) :: [a] -> Int -> a
运算符,对于给定列表和从零开始的索引,给出 i -th element。
所以你可以用以下列表理解来做到这一点:
filterIndex :: [a] -> [Int] -> [a]
filterIndex a b = [a!!(i-1) | i <- b]
然而,效率不高,因为(!!)
在 O(k)中运行 k 索引。通常,如果您使用列表,则会尝试阻止查找 i -th索引。
如果保证b
已排序,您可以通过以下方式提高效率:
-- Only if b is guaranteed to be sorted
filterIndex = filterIndex' 1
where filterIndex' _ _ [] = []
filterIndex' i a:as2 js@(j:js2) | i == j = a : tl js2
| otherwise = tl js
where tl = filterIndex' (i+1) as2
甚至更有效率:
-- Only if b is guaranteed to be sorted
filterIndex = filterIndex' 1
where filterIndex' i l (j:js) | (a:as) <- drop (j-i) l = a : filterIndex' (j+1) as (js)
filterIndex' _ _ [] = []
答案 1 :(得分:2)
我将假设你正在使用b = [0, 2]
(列表在Haskell中被索引为0)。
您可以使用折叠来构建新列表:
selectIndices :: [a] -> [Int] -> [a]
selectIndices as is = foldr (\i bs -> as !! i : bs) [] is
这从一个空列表开始,并使用索引列表as
中的索引i
从is
列表中选择它们来添加新元素。
更高级:如果您更喜欢无点样式,可以编写相同的函数:
selectIndices :: [a] -> [Int] -> [a]
selectIndices as = foldr ((:) . (as !!)) []
如果对索引进行排序可能会更有效的另一种方法是在跟踪当前索引的同时一次遍历列表中的一个元素:
selectIndices :: [a] -> [Int] -> [a]
selectIndices as is = go as 0 (sort is)
where
go :: [a] -> Int -> [Int] -> [a]
go [] _ _ = []
go _ _ [] = []
go (a:as) n (i:is)
| n == i = a : go as (n + 1) is
| otherwise = go as (n + 1) (i:is)
答案 2 :(得分:1)
一种简单的方法是使用索引标记a
中的值,然后根据索引进行过滤:
filterIndex :: [Int] -> [a] -> [a]
filterIndex b = fmap snd . filter (\(i, _) -> i `elem` b) . zip [1..]
-- non-point-free version:
-- filterIndex b a = fmap snd (filter (\(i, _) -> i `elem` b) (zip [1..] a))
(如果您想要从零开始而不是基于一个索引,只需将无限列表更改为[0..]
。您甚至可以使用[initial..]
等参数对其进行参数化。)
如果您需要提高效率,可以考虑使用过滤算法,该算法利用b
中的排序(参见Boomerang和Willem Van Onsem的答案) ,并从zip [1..] a
对列表中构建dictionary。