Удалить элементы по индексу в haskell
Я новичок в Haskell, и я ищу некоторые стандартные функции для работы со списками индексами.
моя конкретная проблема заключается в том, что я хочу удалить 3 элемента через каждые 5. Если его недостаточно ясно, вот иллюстрация:
OOOOOXXXOOOOOXXX...
Я знаю, как написать огромную функцию со многими параметрами, но есть ли умный способ сделать это?
8 ответов
два совершенно разных подхода
-
можно использовать
List.splitAt
вместе сdrop
: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]
. Обратите внимание, что эта функция может быть выражена более изящно, используяuncurry
и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
так как мы используем
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
As Крис Лутц Примечания, это решение может быть обобщено следующим образом:
nofm :: Int -> Int -> [a] -> [a] nofm _ _ [] = [] nofm n m xs = take n xs ++ (nofm n m . drop m) xs
теперь
nofm 5 8
дает требуемую функцию. обратите внимание, что решение сsplitAt
может быть еще более эффективным! -
применить некоторые математики, используя
map
,snd
,filter
,mod
иzip
:f :: [a] -> [a] f = map snd . filter (\(i, _) -> i `mod` 8 < (5 :: Int)) . zip [0..]
идея здесь в том, что мы соединяем каждый элемент в списке с его индексом, натуральным числом я. Затем мы удаляем те элементы, для которых i % 8 > 4. Общая версия этого решения:
nofm :: Int -> Int -> [a] -> [a] nofm n m = map snd . filter (\(i, _) -> i `mod` m < n) . zip [0..]
поскольку никто не делал версию с "unfoldr", вот мой take:
drop3after5 lst = concat $ unfoldr chunk lst
where
chunk [] = Nothing
chunk lst = Just (take 5 lst, drop (5+3) lst)
Кажется, самый короткий до сих пор
Вы можете легко подсчитать свои элементы:
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
на take
и drop
функции могут помочь вам здесь.
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)
Это мое решение. Это очень похоже на @barkmadley это, используя только take
и drop
, но с меньшим беспорядком на мой взгляд:
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>
вот мое решение:
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]