2013-08-01 5 views
0

Как вручную разделить [1,2,4,5,6,7] на [[1],[2],[3],[4],[5],[6],[7]]? Вручную означает без использования пролома.Как разбить список на подсписок в определенных точках?

Затем, как мне разбить список на подсписки в соответствии с предикатом? Например:

f even [[1],[2],[3],[4],[5],[6],[7]] == [[1],[2,3],[4,5],[6,7]] 

PS: Это не домашнее задание, и я пробовал часами, чтобы понять это самостоятельно.

+0

Почему downvotes? Потому что нет кода? – amindfv

+0

@amindfv Я бы так догадался. Если бы ОП продемонстрировал пример того, что они пробовали, он бы продемонстрировал некоторые усилия с их стороны и облегчил бы нам помощь, потому что мы знали бы, какая помощь нужна ОП. Я не голосовал, но если бы у меня был, я бы, вероятно, отказался и добавил комментарий вроде «Что ты пробовал?» – mhwombat

ответ

3

Чтобы ответить на ваш первый вопрос, это скорее элементный подход чем раскол. Соответствующая функция сделать это

map :: (a -> b) -> [a] -> [b] 

Теперь вам нужна функция (a -> b) где b является [a], как вы хотите, чтобы преобразовать элемент в одноплодный список, содержащего один и тот же тип. Вот оно:

mkList :: a -> [a] 
mkList a = [a] 

так

map mkList [1,2,3,4,5,6,7] == [[1],[2],...] 

Что касается вашего второго вопроса: Если вы не можете (домашнее задание?) Использовать break, вы затем разрешается использовать takeWhile и dropWhile, которые образуют как половинки результата break.

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

f p [] = [] 
f p (x:xs) = go [x] xs 
    where go acc [] = [acc] 
      go acc (y:ys) | p y = acc : go [y] ys 
         | otherwise = go (acc++[y]) ys 

Это будет пересекать весь список хвост рекурсивно, всегда помня, что в настоящее время Подсписка есть, и когда вы достигнете элемента, где применяется p, вывод текущего подсписок и запуск нового.

Обратите внимание, что идти первым получает [x] вместо [], чтобы обеспечить для случая, когда первый элемент уже удовлетворяет p x и мы не хотим пустой первый подсписок, который будет выводиться.

Кроме того, это действует на исходный список ([1..7]) вместо [[1],[2]...]. Но вы можете использовать его на преобразованном один, а:

> map concat $ f (odd . head) [[1],[2],[3],[4],[5],[6],[7]] 
[[1,2],[3,4],[5,6],[7]] 
+0

Существует этот «break :: (a -> Bool) -> [a] -> ([a], [a])» и он кажется менее оптимальным, так как он возвращает пару. Предполагается, что 'f' представляет функцию, которая берет предикат' even' и список и возвращает разбиение списка на элементы, для которых предикат, применяемый к элементу, возвращает True. –

+0

@ A.B. Ах! Спасибо за напоминание. – firefrorefiddle

+1

@ A.B., Вы не можете делать то, что хотите, с 'break', поскольку это высокоуровневая конструкция (построенная поверх примитивов отображения нижнего уровня или с рекурсией напрямую), которая не имеет семантики, в которой вы нуждаетесь. Как уже было сказано, лучше всего использовать «карту». –

2

Первая:

map (: []) 

Второй:

f p xs = 
    let rs = foldr (\[x] ~(a:r) -> if (p x) then ([]:(x:a):r) else ((x:a):r)) 
      [[]] xs 
    in case rs of ([]:r) -> r ; _ -> rs 

foldr «s операция достаточно легко визуализировать:

foldr g z [a,b,c, ...,x] = g a (g b (g c (.... (g x z) ....))) 

Таким образом, при записи функции объединения ожидается два аргумента, первый из которых является «текущим элементом» списка, а второй - «результатом обработки остального».Здесь

g [x] ~(a:r) | p x = ([]:(x:a):r) 
      | otherwise = ((x:a):r) 

Так визуализируя ее работать с правой стороны, он просто добавляет в самый последний подсписка и открывает новый подсписок, если это необходимо. Но поскольку списки фактически доступны слева, мы держим его ленивым с ленивым рисунком,     ~(a:r). Сейчас он работает даже на бесконечных списках:

Prelude> take 9 $ f odd $ map (:[]) [1..] 
[[1,2],[3,4],[5,6],[7,8],[9,10],[11,12],[13,14],[15,16],[17,18]] 

Шаблон для 1-го аргумента отражает специфическую структуру ваших ожидаемых входных списков.

+1

Теперь * это * для новичка легко понять ... – firefrorefiddle

+0

@MikeHartl YMMV. :) –

2

Для первых, вы можете использовать список понимание:

>>> [[x] | x <- [1,2,3,4,5,6]] 
[[1], [2], [3], [4], [5], [6]] 

Для второй задачи, вы можете использовать Data.List.Split модуль предоставленный split пакет:

import Data.List.Split 

f :: (a -> Bool) -> [[a]] -> [[a]] 
f predicate = split (keepDelimsL $ whenElt predicate) . concat 

Это первые concat ов список, поскольку функции от split работают над списками, а не списком списков. Итоговый одиночный список - это разделение с использованием функций из разделенного пакета.