Haskell: Разделяне на списък в кортеж от два нови списъка

Имам затруднения да разбера как да разделя списък от Ints в кортеж, съдържащ два нови списъка, така че всеки елемент (започвайки с първия) да влиза в първия списък и всеки друг елемент във втория.

Така:

split [] = ([],[])
split [1] = ([1],[])
split [1,2] = ([1],[2])
split [1,2,3] = ([1,3],[2])
split [1,2,3,4] = ([1,3],[2,4])

Опитвам се да постигна това рекурсивно (с предпазители) и използвам само един аргумент xs

Това е моят подход, който продължава да получава съобщения за грешка:

split :: [Int] -> ([Int],[Int])
split xs | length(xs) == 0 = ([],[])
         | length(xs) == 1 = (xs !! 0 : [],[])
         | length(xs) == 2 = (xs !! 0 : [], xs !! 1 : [])
         | otherwise = (fst ++ xs !! 0, snd ++ xs !! 1) ++ split(drop 2 xs))    

person Shabu    schedule 14.09.2011    source източник
comment
Трябва да приемете един от отговорите.   -  person Ramon Snir    schedule 14.09.2011


Отговори (8)


Вашата функция split връща двойка, но в последния случай използвате ++ за резултата от split. Това ще бъде типова грешка, тъй като ++ работи със списъци, а не с двойки. Има и грешка при типа, защото fst и snd са функции за избиране на елементите на двойка, но вие ги използвате по странен начин.

Освен това използвайте съвпадение на шаблони вместо дължина. Също така, случаят, в който тествате дали дължината е 2, не е необходим, тъй като общият случай премахва 2 елемента, което ви отвежда до основния случай на празния списък.

Можете също да направите функцията си по-обща, като използвате променлива тип a вместо Int в типа.

[Редактиране]: Добавен код

split :: [a] -> ([a], [a])
split [] = ([], [])
split [x] = ([x], [])
split (x:y:xys) = (x:xs, y:ys) where (xs, ys) = split xys
person augustss    schedule 14.09.2011
comment
Като алтернатива, split (z:zs) = (z:ys, xs) where (xs, ys) = split zs! - person Zantier; 09.07.2014

Друг начин да направите това е с взаимна рекурсия. Излиза много лесно за четене:

split xs = (odds xs, evens xs)

odds (x:xs) = x : evens xs
odds xs     = []

evens xs = odds (drop 1 xs)
person Yitz    schedule 14.09.2011

split :: [a] -> ([a], [a])
split xs | null xs = ([], [])
         | otherwise = (head xs : snd pair, fst pair)
  where pair = split (tail xs)

Но трябва да използвате сгъване:

split :: [a] -> ([a], [a])
split = foldr (\x (ys, zs) -> (x : zs, ys)) ([], [])
person dave4420    schedule 14.09.2011
comment
Този код не прави това, което той поиска, и също е лош, защото използва head и tail вместо съвпадение на шаблони. - person augustss; 14.09.2011
comment
Бихте ли дали пример за въвеждане, при което моят код дава грешен резултат, моля. (И имате предвид версията с рекурсия и защита или версията foldr?) Съгласен съм, че съвпадението на шаблони би било по-добро от head и tail, но OP изглежда иска да използва охранители, които --- в този случай --- изключва съвпадението на шаблон (няма какво да се пази след съвпадението на шаблон). - person dave4420; 14.09.2011
comment
Съжалявам, вземам това назад, че е грешно. Няма достатъчно кафе. :) - person augustss; 14.09.2011

Уикито Blow Your Mind на Haskell има няколко реда:

-- splitting in two (alternating)
-- "1234567" -> ("1357", "246")

-- the lazy match with ~ is necessary for efficiency, especially enabling
-- processing of infinite lists
foldr (\a ~(x,y) -> (a:y,x)) ([],[])

(map snd *** map snd) . partition (even . fst) . zip [0..]

transpose . unfoldr (\a -> toMaybe (null a) (splitAt 2 a))
person Thomas Ahle    schedule 07.06.2012

Две алтернативни версии:

split = conv . map (map snd) . groupWith (even.fst) . zip [0..] where
  conv [xs,ys] = (xs,ys)

split xs = (ti even xs, ti odd xs) where
  ti f = map snd . filter (f.fst) . zip [0..]
person Landei    schedule 14.09.2011

Има грешка в последната клауза. Трябва да получите резултати от рекурсивно извикване и след това да добавите първи и втори елемент към тях.

split :: [Int] -> ([Int],[Int])
split xs | length(xs) == 0 = ([],[])
         | length(xs) == 1 = (xs !! 0 : [],[])
         | length(xs) == 2 = (xs !! 0 : [], xs !! 1 : [])
         | otherwise = let (fst, snd) = split(drop 2 xs) in
                     (xs !! 0 : fst, xs !! 1 : snd)
person bravit    schedule 14.09.2011
comment
Това все още е ужасен начин да напишете тази функция. - person augustss; 14.09.2011
comment
Да, абсолютно си прав. Но се опитвам да следвам въпроса „за да постигна това рекурсивно (с предпазители) и използвайки само един аргумент xs“ - person bravit; 14.09.2011
comment
Все още можете да направите това и да получите O(n) сложност вместо O(n^2). - person augustss; 14.09.2011

В случай, че търсите някакъв алтернативен начин да направите това, по-долу е едно такова изпълнение:

split xs = 
     let (a,b) = partition (odd . snd) (zip xs [1..]) 
     in ( (map fst a), (map fst b))
person Ankur    schedule 14.09.2011

Ето едно просто решение:

Като вземем списък [a], можем да го разделим на два нови списъка, първо започваме с деклариране на това, което се случва с празен списък, тук можете да избирате между връщане на грешка Празен списък или просто връщане на два празни списъка, както е показано по-долу , в случай на един елемент в списъка, ние го разделяме на един, съдържащ x и един празен списък ([x],[ ]). В случаите с размер на списъка › 1 ние изчисляваме n (дължината на списъка, разделена на 2), след което „вземаме“ първите n елемента в нов списък и „изтриваме“ n елемента от оригиналния списък xs.

split :: [a] -> ([a],[a])
split [] = ([],[])
split [x] = ([x],[])
split xs = (take n xs, drop n xs)
    where n = (length xs) `div` 2
person FNL    schedule 26.08.2020
comment
Добре дошли в Stack Overflow. Отговорите само с код не се препоръчват в Stack Overflow, защото не обясняват как решава проблема. Моля, редактирайте отговора си, за да обясните какво прави този код и как подобрява другите отговори на въпроса, така че да е полезен за други потребители с подобни проблеми. - person FluffyKitten; 27.08.2020