Haskell通过数值将列表拆分为两个

时间:2013-02-08 07:17:51

标签: haskell

我想通过一个数据透视值将[a]分成([a],[a])并且我有我的代码

splitList :: (Ord a) => a -> [a] -> ([a],[a])
splitList pivot list = 
    ([x | x <- list, x <= pivot], [x | x <- list, x > pivot])

但它迭代列表两次以生成两个列表,有没有办法只迭代一次?

5 个答案:

答案 0 :(得分:6)

有两种可能性,取决于您是否需要tail recursive解决方案(并且不关心反转元素的顺序),或者使用其参数lazily的解决方案。

惰性解决方案决定列表的第一个元素是进入第一个元素还是进入第二个元素,并使用简单的递归来处理列表的其余部分。在大多数情况下,这将是首选解决方案,因为懒惰通常比尾递归更重要:

splitList :: (Ord a) => a -> [a] -> ([a],[a])
splitList _ [] = ([], [])
splitList p (x : xs)
    | x <= p    = (x : l, r)
    | otherwise = (l, x : r)
  where
    ~(l, r) = splitList p xs

然而,在某些情况下,你既不关心元素的排序也不关心懒惰,而是关注速度。 (例如,在实现排序算法时。)然后使用累加器构建结果(请参阅Accumulating Parameters: Getting rid of the 'almost' in "almost tail recursive" )以实现尾递归的变体更合适:

splitListR :: (Ord a) => a -> [a] -> ([a],[a])
splitListR pivot = sl ([], [])
  where
    sl acc    []        = acc
    sl (l, g) (x : xs)
        | x <= pivot    = sl (x : l, g) xs
        | otherwise     = sl (l, x : g) xs

答案 1 :(得分:2)

如果您想从头开始编写,可以维护两个列表,一个用于小项目,一个用于大型项目。首先,我将编写包装器:

splitList :: (Ord a) => a -> [a] -> ([a],[a])
splitList pivot input = spL input [] [] where

好的,所以我只是调用spL并给它两个空列表开始。因为我正在使用where块,我不需要传递枢轴,所以只有三个列表是如果我们没有在输入中留下任何内容,我们已经完成并且应该返回答案:

    spL [] smalls larges = (smalls,larges)

现在你会看到,我们实际上会向后做小和大,所以如果你不喜欢那样,用那里的最后答案对替换(反向小,反向大)。我们现在处理一些输入:

    spL (i:input) smalls larges | i <= pivot = spL input (i:smalls) larges
                                | otherwise  = spL input smalls (i:larges)

所以如果它足够小的话,我们会把它放在小人的前面。

推动列表前面的原因是它每次都会让我们迭代到列表的末尾。如果对你很重要,你可以随时反转以获得原始订单,就像我说的那样。

我们一起得到:

splitList :: (Ord a) => a -> [a] -> ([a],[a])
splitList pivot input = spL input [] [] where
    spL [] smalls larges = (smalls,larges)
    spL (i:input) smalls larges | i <= pivot = spL input (i:smalls) larges
                                | otherwise  = spL input smalls (i:larges)

答案 2 :(得分:2)

通常认为避免手动递归是一种好的风格;相反,你可以使用这样的折叠函数:

splitList pivot = foldr triage ([],[]) 
  where
    triage x ~(lows, highs) 
      | x <= pivot = (x:lows, highs)
      | otherwise  = (lows, x:highs)

当然,使用完全符合您需要的预先存在的功能(即分区)是更好的风格。 :)

答案 3 :(得分:0)

import Data.List (partition)

splitList pivot = partition (<= pivot)

答案 4 :(得分:0)

1976年的

http://www.cs.indiana.edu/pub/techreports/TR27.pdf 1 表明以下内容:

import Control.Applicative

partition3 [] p  = ZipList [[], [], []]
partition3 (x:xs) p 
   | x < p = ZipList [(x:),id,id] <*> partition3 xs p
   | x > p = ZipList [id,id,(x:)] <*> partition3 xs p
   | True  = ZipList [id,(x:),id] <*> partition3 xs p

使用它,我们写

splitList pivot list = (a++b, c)
   where
      [a,b,c] = getZipList $ partition3 list pivot

1 here