List 使用foldl时如何编写take?

List 使用foldl时如何编写take?,list,haskell,fold,List,Haskell,Fold,所以我在做真实世界haskell书中的练习,我使用foldl为takeWhile函数编写了以下代码 myTakeWhile' :: (a->Bool) -> [a] -> [a] myTakeWhile' f xs = foldl step [] xs where step x xs | f x = x:(myTakeWhile' f xs) | otherwise = [] 这会产生以下错误,我无法理解 Could

所以我在做真实世界haskell书中的练习,我使用foldl为takeWhile函数编写了以下代码

myTakeWhile' :: (a->Bool) -> [a] -> [a]
myTakeWhile' f xs = foldl step [] xs
    where step x xs | f x       = x:(myTakeWhile' f xs)
                    | otherwise = []
这会产生以下错误,我无法理解

Couldn't match type ‘a’ with ‘[a]’
      ‘a’ is a rigid type variable bound by
          the type signature for myTakeWhile' :: (a -> Bool) -> [a] -> [a]
          at p1.hs:17:17
    Expected type: [a] -> [a] -> [a]
      Actual type: a -> [a] -> [a]
    Relevant bindings include
      step :: a -> [a] -> [a] (bound at p1.hs:19:11)
      xs :: [a] (bound at p1.hs:18:16)
      f :: a -> Bool (bound at p1.hs:18:14)
      myTakeWhile' :: (a -> Bool) -> [a] -> [a] (bound at p1.hs:18:1)
    In the first argument of ‘foldl’, namely ‘step’
    In the expression: foldl step [] xs
编辑

1) 您的主要错误是,您不理解
fold
为您执行递归,您不必自引用函数
myTakeWhile'
,而且fold还有另一种类型:

foldl: (b -> a -> b) -> b -> [a] -> b
where子句上的step函数中的类型错误是由它引起的

2) 您的第二个错误是,由于
foldl
的定义,您的
takeWhile
将自始至终创建,因此,您必须反转列表(所有这些只是因为您询问如何使用foldl)

您可以通过使用简单的
lambda
if-else
来实现这一点,因为它更具可读性:

myTakeWhile' f xs = foldl (\rs x -> if f x then x:rs else []) [] (reverse xs)
例如:

myTakeWhile' (<10) [1..20]
3)最重要的是:

myTakeWhile'' f xs = foldr (\x rs -> if f x then x:rs else []) [] xs
正如@amalloy所说,
takeWhile
如果您通过使用
foldr
:)来实现它,它会更合适。了解两者之间的差异至关重要。在某些情况下,一个使用一个或另一个会导致性能问题,为此,您可以阅读以下问题:

4)最后,使用
foldr

myTakeWhile'' f xs = foldr (\x rs -> if f x then x:rs else []) [] xs
使用此选项,您可以使用无限列表:

myTakeWhile' (<10) [1..]

这源于

这实现了
过滤器
,而不是
takeWhile
myTakeWhile'f xs=foldr(\x rs->如果f x那么x:rs else[])[]xs工作,虽然我想知道我写的where-step表达式有什么错误,但你也应该提到,
foldl
是用于
takeWhile
的错误折叠:
foldr
更合适。@amalloy这也是事实,我将再次提及,非常感谢!我的意思是,当然,它会起作用,但没有充分的理由,它会变得更糟。你确定这个练习没有要求你使用
foldr
?使用
foldl
实现
takeWhile
毫无意义。使用
foldr
实现它相对简单。练习中没有提到要使用哪个fold。
myTakeWhile' (<10) [1..]
Prelude> foldr (\x y -> concat ["(",x,"+",y,")"]) "0" (map show [1..13])
"(1+(2+(3+(4+(5+(6+(7+(8+(9+(10+(11+(12+(13+0)))))))))))))"

Prelude> foldl (\x y -> concat ["(",x,"+",y,")"]) "0" (map show [1..13])
"(((((((((((((0+1)+2)+3)+4)+5)+6)+7)+8)+9)+10)+11)+12)+13)"