Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
List Haskell:将列表拆分为两个新列表的元组_List_Haskell_Split - Fatal编程技术网

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

List Haskell:将列表拆分为两个新列表的元组,list,haskell,split,List,Haskell,Split,我很难弄清楚如何将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列表拆分为包含两个新列表的元组,这样每个元素(从第一个开始)都会进入第一个列表,第二个列表中的其他元素都会进入

像这样:

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
的结果使用
++
。这将是一个类型错误,因为
++
对列表而不是对工作。还有一个类型错误,因为
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

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

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 :: [a] -> ([a], [a])
split = foldr (\x (ys, zs) -> (x : zs, ys)) ([], [])

如果您正在寻找其他方法来实现这一点,下面是一个这样的实现:

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

两个备选版本:

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

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

split xs = (odds xs, evens xs)

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

evens xs = odds (drop 1 xs)
Haskell 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))

这里有一个简单的解决方案:

通过获取一个列表[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

这仍然是一种可怕的函数编写方式。是的,你完全正确。但我试着按照问题“要递归地完成这一点(使用守卫),并且只使用单参数xs”,您仍然可以这样做,并获得O(n)复杂度,而不是O(n^2)。该代码没有达到他要求的效果,它也不好,因为它使用
而不是模式匹配。请给出一个输入示例,其中我的代码给出了错误的结果。(你指的是递归和保护版本还是foldr版本?)我同意模式匹配要比
好,但OP似乎想使用保护,在这种情况下,保护阻止了模式匹配(在模式匹配后没有任何保护)。对不起,我认为这是错误的。咖啡不够。:)您应该接受其中一个答案。或者,
split(z:zs)=(z:ys,xs),其中(xs,ys)=split zs
!欢迎来到堆栈溢出。对于堆栈溢出,不鼓励使用仅代码的答案,因为它们没有解释堆栈溢出是如何解决问题的。请编辑您的答案,以解释此代码的作用以及它如何改进问题的其他答案,以便对具有类似问题的其他用户有用。
split :: [a] -> ([a],[a])
split [] = ([],[])
split [x] = ([x],[])
split xs = (take n xs, drop n xs)
    where n = (length xs) `div` 2