Создание вариантов кортежей из списка - Haskell

Я относительный новичок в Haskell и пытаюсь создать список кортежей с уравнением, которое я назвал splits который изначально возникает из одного списка, например:

splits [1..4] --> [ ([1],[2,3,4]), ([1,2],[3,4]), ([1,2,3],[4]) ]

или же

splits "xyz" --> [ ("x","yz"), ("xy","z") ]

Создание списка кортежей, которые принимают 1, затем 2, затем 3 элемента и т. Д. Я понял, что, вероятно, я должен использовать функции take/drop, но это то, что у меня есть, и я сталкиваюсь с большим количеством объявлений типов ошибки... есть идеи?

splits :: (Num a) => [a] -> [([a], [a])]

splits [] = error "shortList"

splits [x]
       | length [x] <= 1 = error "shortList"
       | otherwise = splits' [x] 1
         where splits' [x] n = [(take n [x], drop n [x])] + splits' [x] (n+1)

4 ответа

Подход Haskell-y заключается в использовании inits а также tails функции от Data.List:

inits [1,2,3,4] = [ [],        [1],     [1,2], [1,2,3], [1,2,3,4] ]
tails [1,2,3,4] = [ [1,2,3,4], [2,3,4], [3,4], [4],     [] ]

Затем мы просто объединяем эти два списка и удаляем первую пару:

splits xs = tail $ zip (inits xs) (tails xs)

или, что то же самое, сначала удалите первый элемент каждого из списков компонентов:

          = zip (tail (inits xs)) (tail (tails xs))
splits [] = []
splits [_] = []
splits (x:xs) = ([x], xs) : map (\(ys, zs) -> (x:ys, zs)) (splits xs)

У вас есть несколько ошибок.

Вам не нужно иметь Num a класс для a,

использование [] или же [x] в качестве шаблона, но не переменной, используйте xs вместо.

использование ++ вместо + для объединения списков.

В нашем случае используйте (:) добавить список к значению вместо ++,

Добавьте стоп для рекурсии, как дополнительную переменную maxn в splits'

splits :: [a] -> [([a], [a])]
splits [] = error "shortList"
splits xs
       | lxs <= 1 = error "shortList"
       | otherwise = splits' xs 1 lxs
         where
           lxs = length xs
           splits' xs n maxn 
               | n > maxn  = []
               | otherwise = (take n xs, drop n xs) : splits' xs (n+1) maxn

Существует встроенная функция, которая делает часть того, что вы хотите:

splitAt :: Int -> [a] -> ([a], [a])

который делает то, на что он похож

> splitAt 2 [1..4]
([1,2],[3,4])

Используя эту функцию, вы можете просто определить разбиения следующим образом:

splits xs = map (flip splitAt xs) [1..length xs - 1]
Другие вопросы по тегам