【问题标题】:Haskell: Splitting list into tuple of two new listsHaskell:将列表拆分为两个新列表的元组
【发布时间】:2011-11-16 16:20:52
【问题描述】:

我很难弄清楚如何将一个 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))    

【问题讨论】:

  • 你应该接受其中一个答案。

标签: list haskell split


【解决方案1】:

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

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

【讨论】:

  • 欢迎来到 Stack Overflow。在 Stack Overflow 上不鼓励仅使用代码的答案,因为它们没有解释它是如何解决问题的。请编辑您的答案以解释此代码的作用以及它如何改进问题的其他答案,以便对其他有类似问题的用户有用。
【解决方案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))

【讨论】:

    【解决方案3】:

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

    split xs = (odds xs, evens xs)
    
    odds (x:xs) = x : evens xs
    odds xs     = []
    
    evens xs = odds (drop 1 xs)
    

    【讨论】:

      【解决方案4】:

      两个替代版本:

      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】:

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

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

        【讨论】:

          【解决方案6】:

          您的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 (z:zs) = (z:ys, xs) where (xs, ys) = split zs!
          【解决方案7】:
          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)) ([], [])
          

          【讨论】:

          • 那个代码没有按照他的要求做,而且它也很糟糕,因为它使用headtail而不是模式匹配。
          • 您能否举例说明我的代码给出错误结果的输入示例。 (你指的是递归和守卫版本还是折叠版本?)我同意模式匹配会比headtail 更好,但是OP 似乎想要使用守卫,这--- 在这种情况下 --- 排除模式匹配(模式匹配后没有什么需要保护的)。
          • 对不起,我收回了它的错误。咖啡不够。 :)
          【解决方案8】:

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

          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)
          

          【讨论】:

          • 这仍然是一个可怕的方式来编写这个函数。
          • 是的,你完全正确。但我正在尝试遵循问题'以递归方式(使用警卫)完成此任务并且仅使用单个参数 xs'
          • 你仍然可以这样做并获得 O(n) 复杂度而不是 O(n^2)。
          猜你喜欢
          • 1970-01-01
          • 2019-06-05
          • 2014-03-19
          • 1970-01-01
          • 2019-10-20
          • 1970-01-01
          • 1970-01-01
          • 2013-03-25
          • 2022-08-09
          相关资源
          最近更新 更多