【问题标题】:Remove elements by index in haskell在haskell中按索引删除元素
【发布时间】:2010-12-16 16:49:11
【问题描述】:

我是haskell 的新手,我正在寻找一些标准函数来处理按索引列出的列表。

我的确切问题是我想在每 5 个元素之后删除 3 个元素。如果它不够清楚,这里是插图:

OOOOOXXXOOOOOXXX...

我知道如何编写带有许多参数的巨大函数,但是有什么聪明的方法吗?

【问题讨论】:

  • 是的,g n m = map take m . takeWhile (not.null) . unfoldr (Just . splitAt (n+m)) 并将其称为g 3 5 "yourstring"。为unfoldr 导入Data.List

标签: list haskell


【解决方案1】:

两种完全不同的方法

  1. 您可以将List.splitAtdrop 一起使用:

    import Data.List (splitAt)
    f :: [a] -> [a]
    f [] = []
    f xs = let (h, t) = splitAt 5 xs in h ++ f (drop 3 t)
    

    现在f [1..12] 产生[1,2,3,4,5,9,10,11,12]。请注意,使用uncurryControl.Arrow.second 可以更优雅地表达此函数:

    import Data.List (splitAt)
    import Control.Arrow (second)
    f :: [a] -> [a]
    f [] = []
    f xs = uncurry (++) $ second (f . drop 3) $ splitAt 5 xs
    

    由于无论如何我们都在使用Control.Arrow,我们可以选择放弃splitAt,而是在Control.Arrow.(&&&)的帮助下调用,并结合take

    import Control.Arrow ((&&&))
    f :: [a] -> [a]
    f [] = []
    f xs = uncurry (++) $ (take 5 &&& (f . drop 8)) xs
    

    但现在很明显,一个更短的解决方案如下:

    f :: [a] -> [a] 
    f [] = []
    f xs = take 5 xs ++ (f . drop 8) xs
    

    正如Chris Lutz 所说,这个解决方案可以概括如下:

    nofm :: Int -> Int -> [a] -> [a]
    nofm _ _ [] = []
    nofm n m xs = take n xs ++ (nofm n m . drop m) xs
    

    现在nofm 5 8 产生所需的功能。 请注意,使用splitAt 的解决方案可能仍然更有效!

  2. 使用mapsndfiltermodzip 应用一些数学:

    f :: [a] -> [a]
    f = map snd . filter (\(i, _) -> i `mod` 8 < (5 :: Int)) . zip [0..]
    

    这里的想法是我们将列表中的每个元素与其索引配对,一个自然数i。然后我们删除那些 i % 8 > 4 的元素。该解决方案的通用版本是:

    nofm :: Int -> Int -> [a] -> [a]
    nofm n m = map snd . filter (\(i, _) -> i `mod` m < n) . zip [0..]
    

【讨论】:

  • +1 解决方案二很容易推广到NofM :: Int -&gt; Int -&gt; [a] -&gt; [a],用两个参数分别代替 8 和 5。它还具有名称非常明确的优点。
【解决方案2】:

这是我的看法:

deleteAt idx xs = lft ++ rgt
  where (lft, (_:rgt)) = splitAt idx xs

【讨论】:

    【解决方案3】:

    您可以轻松计算您的元素:

    strip' (x:xs) n | n == 7 = strip' xs 0
                    | n >= 5 = strip' xs (n+1)
                    | n < 5 = x : strip' xs (n+1)
    strip l = strip' l 0
    

    虽然开放编码看起来更短:

    strip (a:b:c:d:e:_:_:_:xs) = a:b:c:d:e:strip xs
    strip (a:b:c:d:e:xs) = a:b:c:d:e:[]
    strip xs = xs
    

    【讨论】:

    • 我喜欢第二个版本,但它不是一概而论的。这很不幸,因为这可能是这里最清晰的解决方案。
    • 我喜欢模式匹配解决方案。尽可能简单。尽管您可能需要更多来匹配少于八个元素的列表。永远不记得它是如何工作的。
    【解决方案4】:

    由于没有人使用“unfoldr”制作版本,因此这是我的看法:

    drop3after5 lst = concat $ unfoldr chunk lst
      where
        chunk [] = Nothing
        chunk lst = Just (take 5 lst, drop (5+3) lst)
    

    似乎是迄今为止最短的

    【讨论】:

    • 最终结果需要Concat函数。
    【解决方案5】:

    takedrop 函数可能会在这里为您提供帮助。

    drop, take :: Int -> [a] -> [a]
    

    从这些我们可以构造一个函数来执行一个步骤。

    takeNdropM :: Int -> Int -> [a] -> ([a], [a])
    takeNdropM n m list = (take n list, drop (n+m) list)
    

    然后我们可以用它来减少我们的问题

    takeEveryNafterEveryM :: Int -> Int -> [a] -> [a]
    takeEveryNafterEveryM n m [] = []
    takeEveryNafterEveryM n m list = taken ++ takeEveryNafterEveryM n m rest
        where
            (taken, rest) = takeNdropM n m list
    
    *Main> takeEveryNafterEveryM 5 3 [1..20]
    [1,2,3,4,5,9,10,11,12,13,17,18,19,20]
    

    由于这不是递归的原始形式,因此很难将其表示为简单的折叠。

    因此可以定义一个新的折叠函数来满足您的需求

    splitReduce :: ([a] -> ([a], [a])) -> [a] -> [a]
    splitReduce f []   = []
    splitReduce f list = left ++ splitReduce f right
        where
            (left, right) = f list
    

    那么takeEveryNafterEveryM的定义很简单

    takeEveryNafterEveryM2 n m = splitReduce (takeNdropM 5 3)
    

    【讨论】:

      【解决方案6】:

      这是我的解决方案。这很像@barkmadley's answer,只使用takedrop,但在我看来没有那么混乱:

      takedrop :: Int -> Int -> [a] -> [a]
      takedrop _ _ [] = []
      takedrop n m l  = take n l ++ takedrop n m (drop (n + m) l)
      

      不确定它是否会因速度或聪明而赢得任何奖项,但我认为它非常清晰简洁,而且确实有效:

      *Main> takedrop 5 3 [1..20]
      [1,2,3,4,5,9,10,11,12,13,17,18,19,20]
      *Main> 
      

      【讨论】:

        【解决方案7】:

        这是我的解决方案:

        remElements step num=rem' step num
            where rem' _ _ []=[]
                  rem' s n (x:xs)
                      |s>0 = x:rem' (s-1) num xs
                      |n==0 = x:rem' (step-1) num xs
                      |otherwise= rem' 0 (n-1) xs
        

        示例:

        *Main> remElements 5 3 [1..20]
        [1,2,3,4,5,9,10,11,12,13,17,18,19,20]
        

        【讨论】:

          【解决方案8】:
          myRemove = map snd . filter fst . zip (cycle $ (replicate 5 True) ++ (replicate 3 False))
          

          【讨论】:

            猜你喜欢
            • 2020-01-02
            • 2017-12-16
            • 1970-01-01
            • 1970-01-01
            • 2016-05-20
            • 1970-01-01
            • 2010-10-12
            • 2010-10-26
            相关资源
            最近更新 更多