Есть ли операция в списках в библиотеке, которая делает группы из n элементов? Например: n = 3
groupInto 3 [1,2,3,4,5,6,7,8,9] = [[1,2,3],[4,5,6],[7,8,9]]
Если нет, как это сделать?
Есть ли операция в списках в библиотеке, которая делает группы из n элементов? Например: n = 3
groupInto 3 [1,2,3,4,5,6,7,8,9] = [[1,2,3],[4,5,6],[7,8,9]]
Если нет, как это сделать?
Быстрый поиск по Hoogle показал, что такой функции нет. С другой стороны, ответили, что в пакете split
есть один, который называется chunksOf
.
Однако вы можете сделать это самостоятельно
group :: Int -> [a] -> [[a]]
group _ [] = []
group n l
| n > 0 = (take n l) : (group n (drop n l))
| otherwise = error "Negative n"
Конечно, некоторые круглые скобки можно удалить, я оставил их здесь, чтобы понять, что делает код:
Основной случай прост: всякий раз, когда список пуст, просто возвращайте пустой список.
Сначала рекурсивные тесты, если n
положительно. Если n
0
или ниже, мы вводим бесконечный цикл, и мы этого не хотим. Затем мы разбиваем список на две части, используя take
и drop
: take
возвращает первые n
элементы, а drop
возвращает остальные. Затем мы добавляем первые n
элементы в список, полученный путем применения нашей функции к другим элементам в исходном списке.
Эта функция, среди других аналогичных, может быть найдена в популярном пакете split.
> import Data.List.Split
> chunksOf 3 [1,2,3,4,5,6,7,8,9]
[[1,2,3],[4,5,6],[7,8,9]]
Вы можете написать его сами, как отметил Михай. Но я бы использовал функцию splitAt, так как она не требует двух проходов в списке ввода, например, -drop:
chunks :: Int -> [a] -> [[a]]
chunks _ [] = []
chunks n xs =
let (ys, zs) = splitAt n xs
in ys : chunks n zs
Это общий шаблон - генерирование списка из начального значения (которое в этом случае является вашим списком ввода) с помощью повторной итерации. Этот шаблон фиксируется в функции unfoldr. Мы можем использовать его со слегка измененной версией splitAt (спасибо Will Ness для более сжатой версии):
chunks n = takeWhile (not.null) . unfoldr (Just . splitAt n)
То есть мы генерируем фрагменты из n элементов, в то же время мы сокращаем входной список на n, используя unfoldr, и мы генерируем эти куски, пока не получим пустой список - в этот момент исходный вход полностью потребляется.
Конечно, как указывали другие, вы должны использовать уже существующую функцию из разделенного модуля. Но всегда полезно приучать себя к функциям обработки списков в стандартных библиотеках Haskell.
Это свойство называется "chunk" и является одним из наиболее часто упоминаемых операций с списком, не находящимся в base
. Пакет split
предоставляет такую операцию, хотя копирует и вставляет документацию на пикши:
> chunksOf 3 ['a'..'z']
["abc","def","ghi","jkl","mno","pqr","stu","vwx","yz"]
Кроме того, в отличие от моих пожеланий, hoogle ищет только небольшой набор библиотек (те, которые поставляются с GHC или, возможно, HP), но вы можете явно добавлять пакеты к поиску с помощью +PKG_NAME
- hoogle с Int -> [a] -> [[a]] +split
получает то, что вы хотите, Некоторые люди используют Hayoo по этой причине.