如何使用foldl编写takeWhile?

时间:2017-07-09 05:43:11

标签: 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 = []

这会出现以下错误,我无法理解。

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 个答案:

答案 0 :(得分:4)

  

修改

1)你的主要错误是,你不理解fold为你做递归,你不必自我引用函数myTakeWhile',折叠还有另一个输入:

foldl: (b -> a -> b) -> b -> [a] -> b

你在where子句的步骤函数中的类型错误是由于它。

2)您的第二个错误,由于foldl的定义,您的takeWhile将从头到尾创建,因此,您必须撤消列表(全部这只是因为你在询问如何使用foldl)

您可以使用简单的lambdaif else执行此操作,更具可读性:

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

示例:

myTakeWhile' (<10) [1..20]
  

[1,2,3,4,5,6,7,8,9]

然而,有了这个,你就无法查看无限列表,如下所示:

myTakeWhile' (<10) [1..]

它会挂起来试图创建所有未评估的表达式。

无论如何,如果你想使用guardswhere,你应该这样做:

myTakeWhile f xs = foldl step [] (reverse xs)
  where step rs x | f x = x : rs
                  | otherwise = []

3)最重要的是:

正如@amalloy所说,takeWhile更适合使用 foldr :)来实现它。理解两者之间的差异是至关重要的,在某些情况下,使用一个或另一个可能会导致性能问题,为此,您可以阅读此问题:

foldl versus foldr behavior with infinite lists

4)最后,使用foldr

的正确替代方案
myTakeWhile'' f xs = foldr (\x rs -> if f x then x:rs else []) [] xs

有了这个,你可以使用无限列表:

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)"

来自https://wiki.haskell.org/Fold

的来源