Есть ли встроенная функция для получения всех последовательных подпоследовательностей размера n списка в Haskell?
Например, мне нужна функция:
gather :: Int -> [a] -> [[a]]
gather n list = ???
где gather 3 "Hello!" == ["Hel","ell","llo","ol!"]
,
У меня есть рабочая реализация:
gather :: Int-> [a] -> [[a]]
gather n list =
unfoldr
(\x ->
if fst x + n > length (snd x) then
Nothing
else
Just
(take
n
(drop
(fst x)
(snd x)),
(fst x + 1, snd x)))
(0, list)
но мне интересно, есть ли что-то уже встроенное в язык для этого? Я отсканировал Data.List, но ничего не увидел.
2 ответа
Вы могли бы использовать tails
:
gather n l = filter ((== n) . length) $ map (take n) $ tails l
или используя takeWhile
вместо filter
:
gather n l = takeWhile ((== n) . length) $ map (take n) $ tails l
РЕДАКТИРОВАТЬ: Вы можете удалить шаг фильтра, удалив последний n
элементы списка, возвращаемые из tails
как предложено в комментариях:
gather n = map (take n) . dropLast n . tails
where dropLast n xs = zipWith const xs (drop n xs)
Сбрасывание хвостов может быть организовано автоматически благодаря свойствам застежки-молнии,
import Data.List (tails)
g :: Int -> [a] -> [[a]]
g n = foldr (zipWith (:)) (repeat []) . take n . tails
или просто transpose . take n . tails
было бы достаточно. Тестирование:
Прелюдия Data.List> g 3 [1..10]
[[1,2,3], [2,3,4], [3,4,5], [4,5,6], [5,6,7], [6,7,8], [ 7,8,9], [8,9,10]]
Прелюдия Data.List> транспонировать. возьми 3. хвосты $ [1..10]
[[1,2,3], [2,3,4], [3,4,5], [4,5,6], [5,6,7], [6,7,8], [ 7,8,9], [8,9,10], [9,10], [10]]
(редактировать 2018-09-16:) Использование zip может быть выражено на более высоком уровне, с traverse ZipList
:
g :: Int -> [a] -> [[a]]
g n = getZipList . traverse ZipList . take n . tails