繁体   English   中英

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

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

我很难弄清楚如何将一个 Int 列表拆分成一个包含两个新列表的元组,这样每个元素(从第一个开始)进入第一个列表,每个其他元素进入第二个列表。

像这样:

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

您的split函数返回一对,但在最后一种情况下,您在split的结果上使用++ 这将是一个类型错误,因为++适用于列表,而不是对。 还有一个类型错误,因为fstsnd是挑选一对元素的函数,但是你使用它们是一种奇怪的方式。

此外,使用模式匹配而不是使用长度。 此外,不需要测试长度是否为 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

另一种方法是使用相互递归。 它很容易阅读:

split xs = (odds xs, evens xs)

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

evens xs = odds (drop 1 xs)

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)

但是你应该使用折叠:

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

两个替代版本:

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

这是一个简单的解决方案:

通过获取列表 [a],我们可以将其拆分为两个新列表,首先我们首先声明空列表会发生什么,在这里您可以选择返回错误“空列表”或仅返回两个空列表作为如下所示,在列表中有一个元素的情况下,我们将其拆分为一个包含 x 的列表和一个空列表 ([x],[ ])。 在列表大小 > 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

最后一句有错误。 您必须从递归调用中获取结果,然后向其中添加第一个和第二个元素。

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)

如果您正在寻找某种替代方法来执行此操作,以下是一种这样的实现:

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