简体   繁体   English

Haskell:将列表拆分为两个新列表的元组

[英]Haskell: Splitting list into tuple of two new lists

I am having difficulty figuring out how to split a list of Ints into a tuple containing two new lists, such that every element (starting with first) goes into the first list and every other element in the second.我很难弄清楚如何将一个 Int 列表拆分成一个包含两个新列表的元组,这样每个元素(从第一个开始)进入第一个列表,每个其他元素进入第二个列表。

Like so:像这样:

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])

I'm trying to accomplish this recursively(with guards) and only using the single argument xs我正在尝试以递归方式(使用警卫)完成此操作,并且仅使用单个参数 xs

This is my approach that keeps getting error messages:这是我不断收到错误消息的方法:

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))    

Your split function returns a pair, but in the last case you are using ++ on the result of split .您的split函数返回一对,但在最后一种情况下,您在split的结果上使用++ That will be a type error, since ++ works on lists, not pairs.这将是一个类型错误,因为++适用于列表,而不是对。 There is also a type error because fst and snd are functions to pick out the elements of a pair, but you are using them is a strange way.还有一个类型错误,因为fstsnd是挑选一对元素的函数,但是你使用它们是一种奇怪的方式。

Furthermore, use pattern matching instead of using length.此外,使用模式匹配而不是使用长度。 Also, the case where you test if the length is 2 is not needed, since the general case removes 2 elements which takes you down to the base case of the empty list.此外,不需要测试长度是否为 2 的情况,因为一般情况会删除 2 个元素,这会将您带到空列表的基本情况。

You can also make your function more general by using a type variable a instead of Int in the type.您还可以通过在类型中使用类型变量a而不是Int来使您的函数更通用。

[Edit]: Added code [编辑]:添加代码

split :: [a] -> ([a], [a])
split [] = ([], [])
split [x] = ([x], [])
split (x:y:xys) = (x:xs, y:ys) where (xs, ys) = split xys

Another way to do this is with mutual recursion.另一种方法是使用相互递归。 It comes out very easy to read:它很容易阅读:

split xs = (odds xs, evens xs)

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

evens xs = odds (drop 1 xs)

The Haskell Blow Your Mind wiki, has some one liners: Haskell Blow Your Mind wiki 有一些内衬:

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

But you should be using a fold:但是你应该使用折叠:

split :: [a] -> ([a], [a])
split = foldr (\x (ys, zs) -> (x : zs, ys)) ([], [])

Two alternative versions:两个替代版本:

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..]

Here is a simple solution:这是一个简单的解决方案:

By taking a list [a] we are able to split it in two new list, first we start off by declaring what happens with a empty list, here you can choose between returning an error "Empty list" or just returning two empty list as shown below, in the case of one element in list we split it in one containing x and one empty list ([x],[ ]).通过获取列表 [a],我们可以将其拆分为两个新列表,首先我们首先声明空列表会发生什么,在这里您可以选择返回错误“空列表”或仅返回两个空列表作为如下所示,在列表中有一个元素的情况下,我们将其拆分为一个包含 x 的列表和一个空列表 ([x],[ ])。 In cases with list size > 1 we calculate n (length of the list "divided by" 2), then we 'take' the first n elements in a new list and 'drop' the n elements from the original list xs.在列表大小 > 1 的情况下,我们计算 n(列表的长度“除以”2),然后我们“获取”新列表中的前 n 个元素并从原始列表 xs 中“删除”n 个元素。

split :: [a] -> ([a],[a])
split [] = ([],[])
split [x] = ([x],[])
split xs = (take n xs, drop n xs)
    where n = (length xs) `div` 2

There is a mistake in the last clause.最后一句有错误。 You have to get results from recursive call and then add first and second elements to them.您必须从递归调用中获取结果,然后向其中添加第一个和第二个元素。

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)

In case you are looking for some alternate way to do this, below is one such implementation:如果您正在寻找某种替代方法来执行此操作,以下是一种这样的实现:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM