我使用以下折叠来获得列表的最终单调递减序列。
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
类型,您如何将此折叠设为通用?
答案 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的内容,因为它可以帮助您找出类似错误的错误。