Remove elements by index in haskell

后端 未结 8 882

I\'m new in haskell and I\'m looking for some standard functions to work with lists by indexes.

My exact problem is that i want to remove 3 elements after every 5. If i

相关标签:
8条回答
  • 2021-02-07 23:37

    Since nobody did a version with "unfoldr", here is my take:

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

    Seems to be the shortest thus far

    0 讨论(0)
  • 2021-02-07 23:47

    the take and drop functions may be able to help you here.

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

    from these we could construct a function to do one step.

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

    and then we can use this to reduce our problem

    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]
    

    since this is not a primitive form of recursion, it is harder to express this as a simple fold.

    so a new folding function could be defined to fit your needs

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

    then the definition of takeEveryNafterEveryM is simply

    takeEveryNafterEveryM2 n m = splitReduce (takeNdropM 5 3)
    
    0 讨论(0)
  • 2021-02-07 23:49

    Two completely different approaches

    1. You can use List.splitAt together with drop:

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

      Now f [1..12] yields [1,2,3,4,5,9,10,11,12]. Note that this function can be expressed more elegantly using uncurry and Control.Arrow.second:

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

      Since we're using Control.Arrow anyway, we can opt to drop splitAt and instead call in the help of Control.Arrow.(&&&), combined with take:

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

      But now it's clear that an even shorter solution is the following:

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

      As Chris Lutz notes, this solution can then be generalized as follows:

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

      Now nofm 5 8 yields the required function. Note that a solution with splitAt may still be more efficient!

    2. Apply some mathematics using map, snd, filter, mod and zip:

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

      The idea here is that we pair each element in the list with its index, a natural number i. We then remove those elements for which i % 8 > 4. The general version of this solution is:

      nofm :: Int -> Int -> [a] -> [a]
      nofm n m = map snd . filter (\(i, _) -> i `mod` m < n) . zip [0..]
      
    0 讨论(0)
  • 2021-02-07 23:49

    Here is my solution:

    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
    

    example:

    *Main> remElements 5 3 [1..20]
    [1,2,3,4,5,9,10,11,12,13,17,18,19,20]
    
    0 讨论(0)
  • 2021-02-07 23:51

    You can count your elements easily:

    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
    

    Though open-coding looks shorter:

    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
    
    0 讨论(0)
  • 2021-02-07 23:53

    Here is my take:

    deleteAt idx xs = lft ++ rgt
      where (lft, (_:rgt)) = splitAt idx xs
    
    0 讨论(0)
提交回复
热议问题