haskell 从一个列表中提取具有另一个列表中给定索引的元素

wtlkbnrh  于 2022-11-14  发布在  其他
关注(0)|答案(4)|浏览(166)

所以我需要从给定的列表中提取元素,这些元素的索引在另一个列表中给出。签名应该是这样的:

search :: [Int] -> [a] -> [a]

并且结果

search [1,3,5] [34,65,67,34,23,43,54]
[65,34,43]

据我所知,没有标准的函数,我可以用循环在更常见的语言上做这个,但是我不太擅长haskell。

csga3l58

csga3l581#

假设索引是排序的,您可以编写自己的显式递归。

search :: [Int] -> [a] -> [a]
search indices xs = go indices 0 xs  -- start from index 0
   where
   go :: [Int] -> Int -> [a] -> [a]
   -- no more indices, we are done
   go []     _ _                = []
   -- more indices but no more elements -> error
   go _      _ []               = error "index not found"
   -- if the wanted index i is the same as the current index j,
   -- return the current element y, more to the next wanted index
   go (i:is) j yys@(y:_) | i==j = y : go is  j     yys
   -- otherwise, skip y and increment the current index j
   go iis    j (_:ys)           =     go iis (j+1) ys

这是一个复杂度为O(n)的过程,其中n是列表的长度,而n是列表的长度,所以这个过程的时间复杂度为O(n)。
注意,重复调用!!将需要O(n^2)的时间,因为每个!!花费O(n)。
时间复杂度为O(n log n),因此,时间复杂度为O(n log n)。

643ylb08

643ylb082#

我们可以使用(!!) :: [a] -> Int -> Int运算符和 list comprehension 来实现这一点,如下所示:

search :: [Int] -> [a] -> [a]
search js xs = [xs!!j | j <- js]

但是(!!)操作符在 O(k) 中工作,k 是请求的索引,所以这将是低效的。
给定索引列表是有序的并且没有超出界限,纯Haskell函数可以是如下:

search :: [Int] -> [a] -> [a]
search = search' 0
    where search' _ []     _  = []
          search' i (j:js) xs = y : search' (j+1) js ys
              where (y:ys) = drop (j-i) xs
ovfsdjhp

ovfsdjhp3#

可以使用!! operator访问列表的元素,如下所示:

List!!index == value_at_that index

因此,您的函数可能如下所示:

search indexes list = [list!!x | x <- indexes]
6fe3ivhb

6fe3ivhb4#

基于库的解决方案:

当面对这类Haskell工作时,为它找到一个合适的库函数通常是一个好主意。与临时手动递归不同,可以信任正式的库函数与Haskell运行时系统的特性平滑地交互。
在这里,手头的工作似乎非常适合unfoldr库函数:

unfoldr :: (st -> Maybe (a, st)) -> st -> [a]

unfoldr函数采用a)初始 * 状态 * 和B)* 状态转移函数 *,其产生(可能)新状态以及输出值。
这里,该状态可以由包含以下的三元组组成:
1.初始值列表的深度
1.索引列表的剩余部分
1.值列表的剩余部分
作为预备步骤,我们暂时假设索引列表是有序的,因此我们可以以线性方式处理两个列表(下面将进一步解除该限制)。
这将给出以下代码:

import Data.List (unfoldr)

search1 :: [Int] -> [a] -> [a]
search1 indices xs  =  unfoldr  stepFn  state0
  where
    state0 = (0, indices, xs)
    stepFn (_, [], _)         =  Nothing  -- no indices left, hence the end
    stepFn (d, idx:idxs, xs)  =  let
                                      ofs  = idx - d
                                      rest = drop ofs xs
                                 in
                                      Just ( head rest,
                                            (idx, idxs, rest) )

测试:

$ ghci
GHCi, version 8.8.4: https://www.haskell.org/ghc/  :? for help
 λ> 
 λ> :load q72853867.hs
 [1 of 1] Compiling Main             ( q72853867.hs, interpreted )
 Ok, one module loaded.
 λ> 
 λ> search1  [0,0,2,5,5,9,12]  [0..12]
 [0,0,2,5,5,9,12]
 λ>

这是可行的。现在我们来看看如何解除有序索引的限制。

允许无序的索引列表:

这里的想法是对索引列表进行 * 排序 *,但要以一种跟踪任何给定索引在初始列表中所占 * 排名 * 的方式来进行。
因此,我们可以编写一个search2p辅助函数,该函数生成所请求的值,但与它们对应的索引排名配对。接下来,我们提供一个search2 Package 函数,该函数恢复初始索引顺序并丢弃排名信息:

import Data.List (unfoldr, sortBy)
import Data.Ord  (comparing)

search2p :: [Int] -> [a] -> [(Int,a)]
search2p indices xs  =  unfoldr  stepFn  state0
  where
    state0 = (0,  sortBy (comparing fst) (zip indices [(0::Int)..]),  xs)
    stepFn (_, [], _)         =  Nothing  -- no indices left, hence the end
    stepFn (d, (idx, rank):idxs, xs)  =  let
                                             ofs  = idx - d
                                             rest = drop ofs xs
                                         in
                                             Just ( (rank, head rest) ,
                                                    (idx, idxs, rest) )

search2 :: [Int] -> [a] -> [a]
search2 indices xs = map snd $ sortBy (comparing fst) $ search2p indices xs

**注意:**这类似于某些圈子中称为decorate-sort-undecorate paradigm的技术。

测试:

λ> 
 λ> search2p  [0,3,0,5,12,1,1]  [0..12]
 [(0,0),(2,0),(5,1),(6,1),(1,3),(3,5),(4,12)]
 λ> 
 λ> search2  [0,3,0,5,12,1,1]  [0..12]
 [0,3,0,5,12,1,1]
 λ>

相关问题