将foldl转换为fold1

时间:2017-04-01 21:32:19

标签: haskell fold

我使用以下折叠来获得列表的最终单调递减序列。

foldl (\acc x -> if x<=(last acc) then acc ++ [x] else [x]) [(-1)] a

因此[9,5,3,6,2,1]会返回[6,2,1] 但是,foldl我需要为折叠提供一个开头[(-1)]。我试图转向foldl1以便能够处理任何范围的整数以及任何Ord a如此:

foldl1 (\acc x -> if x<=(last acc) then acc ++ [x] else [x]) a

但我得到了错误:

cannot construct infinite type: a ~ [a]
in the second argument of (<=) namely last acc

我的印象是foldl1基本上是:

foldl (function) [head a] a

但我想这不是吗?对于任何Ord类型,您如何将此折叠设为通用?

2 个答案:

答案 0 :(得分:3)

  

我的印象是foldl1基本上是:

foldl (function) [head a] a

foldl1基本上是:

foldl function (head a) (tail a)

因此初始元素不是head a的列表,而是head a

  

对于任何fold类型,您如何制作此Ord通用名称?

快速解决方法是:

foldl (\acc x -> if x<=(last acc) then acc ++ [x] else [x]) [head a] (tail a)

但仍有两个问题:

  • 如果a空列表,此函数将出错(您可能希望返回空列表);和
  • 由于last(++)都在 O(n)中运行,因此代码效率不高。

使用模式匹配可以轻松解决第一个问题,以防止出现这种情况。但对于后者,你最好使用reverse方法。比如:

f :: Ord t => [t] -> [t]
f [] = [] -- case when the empty list is given
f a = reverse $ foldl (\acc@(ac:_) x -> if x <= ac then (x:acc) else [x]) [head a] (tail a)

此外,我个人在函数式编程中并不是if - then - else的忠实粉丝,例如,您可以定义一个辅助函数,如:

f :: Ord t => [t] -> [t]
f [] = [] -- case when the empty list is given
f a = reverse $ foldl g [head a] (tail a)
    where g acc@(ac:_) x | x <= ac = (x:acc)
                         | otherwise = [x]

现在reverse O(n)中运行,但这只是一次。此外,(:)构造在 O(1)中运行,因此g中的所有操作都在 O(1)中运行(很好地给出了比较课程效率很高,等等)使算法本身 O(n)

对于您的样本输入,它给出:

*Main> f [9,5,3,6,2,1]
[6,2,1]

答案 1 :(得分:2)

foldl1的类型是:

Foldable t => (a -> a -> a) -> t a -> a

你的函数参数,

\acc x -> if x<=(last acc) then acc ++ [x] else [x]

有类型:

(Ord a) => [a] -> a -> [a]

当Haskell的类型检查器尝试对你的函数进行类型检查时,它会尝试将a -> a -> a类型(foldl1的第一个参数的类型)与类型[a] -> a -> [a](你的类型)统一起来功能)。

要统一这些类型,需要将a[a]统一,这将导致无限类型a ~ [a] ~ [[a]] ~ [[[a]]]...,依此类推。

使用foldl时这样做的原因是foldl的类型是:

Foldable t => (b -> a -> b) -> b -> t a -> b

因此[a]b统一,a与其他a统一,导致完全无问题。

foldl1的局限性在于它只能处理只处理一种类型的函数,或者,换句话说,累加器需要与输入列表的类型相同(例如,折叠列表时)在Int s中,foldl1只能返回Int,而foldl可以使用任意累加器。所以你不能使用foldl1)。< / p>

关于为所有Ord值制作此泛型,一种可能的解决方案是为值创建一个新的类型类,这些值表示它们自己的“最小约束”值,然后由函数使用。您无法创建此函数,因为它在所有Ord值上都是通用的,因为并非所有Ord值都具有您可以使用的序列最小边界。

class LowerBounded a where
    lowerBound :: a

instance LowerBounded Int where
    lowerBound = -1

finalDecreasingSequence :: (Ord a, LowerBounded a) => [a] -> [a]
finalDecreasingSequence = foldl buildSequence lowerBound
  where buildSequence acc x
    | x <= (last acc) = acc ++ [x]
    | otherwise       = [x]

您可能还想阅读一些关于how Haskell does its type inference的内容,因为它可以帮助您找出类似错误的错误。