我很难弄清楚如何将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))
答案 0 :(得分:14)
您的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
答案 1 :(得分:5)
另一种方法是使用相互递归。它很容易阅读:
split xs = (odds xs, evens xs)
odds (x:xs) = x : evens xs
odds xs = []
evens xs = odds (drop 1 xs)
答案 2 :(得分:3)
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)) ([], [])
答案 3 :(得分:2)
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))
答案 4 :(得分:1)
两个替代版本:
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..]
答案 5 :(得分:0)
最后一句中有错误。你必须从递归调用中获得结果,然后向它们添加第一个和第二个元素。
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)
答案 6 :(得分:0)
如果您正在寻找其他方法来执行此操作,下面是一个这样的实现:
split xs =
let (a,b) = partition (odd . snd) (zip xs [1..])
in ( (map fst a), (map fst b))
答案 7 :(得分: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