【问题标题】:Selecting string elements in a list using integer elements from another list使用另一个列表中的整数元素选择列表中的字符串元素
【发布时间】:2017-03-26 22:10:00
【问题描述】:

我将用一个例子来解释我的问题,因为我不确定用语言表达的最佳方式。

假设我有两个列表 ab

a = ["car", "bike", "train"]b = [1, 3]

我想通过选择a中的位置与b中的整数相对应的项目来创建一个新列表c,所以列出c = ["car", "train"]

我将如何在 Haskell 中执行此操作?我想我必须使用列表理解,但不确定如何。干杯。

【问题讨论】:

  • b 是否保证排序?

标签: list haskell dictionary


【解决方案1】:

直接的方法是使用(!!) :: [a] -> Int -> a运算符,对于给定的列表和从零开始的索引,给出i第 em> 个元素。

因此,您可以使用以下列表理解来做到这一点:

filterIndex :: [a] -> [Int] -> [a]
filterIndex a b = [a!!(i-1) | i <- b]

但是,这是效率不高,因为(!!)O(k) 中运行,而k 是索引。通常,如果您使用 lists,您会尽量避免查找第 i 个索引。


如果保证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' _ _ [] = []

【讨论】:

  • 在你的最后一个例子中,你是否使用保护来避免嵌套的 where 子句?
  • 这不是本意,但这确实是一个很好的副作用:它是一个模式保护。这样,如果以某种方式丢弃导致空列表 [] 我们知道有问题。
【解决方案2】:

我假设您使用的是b = [0, 2](列表在 Haskell 中索引为 0)。

您可以使用折叠来构建新列表:

selectIndices :: [a] -> [Int] -> [a]
selectIndices as is = foldr (\i bs -> as !! i : bs) [] is

这从一个空列表开始,并通过使用索引列表is 中的索引ias 列表中选择它们来添加新元素。

更高级:如果您更喜欢 point-free 样式,可以编写相同的函数:

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)

【讨论】:

  • 请注意,使用i-1 可以轻松使用基于一的索引。但是 +1 :)
【解决方案3】:

一个简单的方法是用索引标记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 中的排序的过滤算法(参见BoomerangWillem Van Onsem 的答案),并构建一个@987654323 @来自zip [1..] a 对列表。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-19
    相关资源
    最近更新 更多