Когда я вижу такую проблему, мне нравится думать о «форме» функции, которую вы хотите. В этом случае вы начинаете со списка и составляете список списков. Это частный случай, начинающийся с одного значения и создающий список, который соответствует разворачивается. Итак, если вы не возражаете импортировать Data.List
, вы можете аккуратно написать свою функцию, используя unfoldr
.
Давайте посмотрим, как это сделать шаг за шагом. Во-первых, как и выше, вы просто должны знать о unfoldr
и видеть, что он может применяться. Здесь немного опыта (например, чтение ответов, как этот :)) приходит в
Далее мы посмотрим на тип unfoldr
:.
Prelude Data.List> :t unfoldr
unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
Идея заключается в том, что мы начинаем с одно значение «ядро» (b
) и повторно нажимайте функцию шага (b -> Maybe (a, b)
), пока мы не нажмем Nothing
. Наша стартовая ценность - это список, который мы хотим разделить на куски, поэтому нам больше не нужно заниматься обработкой. Это означает, что последний шаг, который мы выполняем, - это реализация функции шага.
Поскольку мы начинаем со списка значений, мы можем заменить b
на [n]
; более того, поскольку мы хотим составить список списков в самом конце, мы можем заменить [a]
на [[n]]
и, следовательно, a
с [n]
. Это дает нам окончательный вид, мы должны реализовать для ступенчатой функции:
[n] -> Maybe ([n], [n])
Эй, что очень похож на тип splitAt
!
splitAt :: Int -> a -> ([a], [a])
На самом деле, мы можем просто обернуть результат splitAt
в Just
и отвечают соответствующим требованиям наших типов. Но это оставляет очень важную роль: финальный Nothing
, который сообщает unfoldr
, чтобы остановиться! Поэтому наша вспомогательная функция нуждается в дополнительном базовом футляре для []
, чтобы вернуть правильный результат.
Собирает все вместе дает нам окончательную функцию:
import Data.List (unfoldr)
paginate n = unfoldr go
where go [] = Nothing -- base case
go ls = Just (splitAt n ls)
Я думаю, конечный результат довольно хорошо, но я не уверен, что это больше, чем читаемые рекурсивная версия.
Если вы не возражаете, позволяя расширение (LambdaCase
), вы можете переписать это в несколько аккуратным способом, избегая имен go
:
paginate n = unfoldr $ \case
[] -> Nothing
ls -> Just (splitAt n ls)
'Data.List. Split' из пакета 'split' имеет' chunksOf', если вы хотите, чтобы он уже был реализован. – bheklilr