Есть ли встроенная функция для получения всех последовательных подпоследовательностей размера 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
Другие вопросы по тегам