2011-01-19 77 views
5

我是Haskell的新手,我正在阅读本书"Real World Haskell"。在本书的第4章中,作者要求使用fold来重写groupBy函数。一书(屋大维沃伊库)的读者提供了以下解决方案:有多少个参数需要Haskell的foldr函数?


theCoolGroupBy :: (a -> a -> Bool) -> [a] -> [[a]] 
theCoolGroupBy eq xs = tail $ foldr step (\_ -> [[]]) xs $ (\_ -> False) 
         where step x acc = \p -> if p x then rest p else []:rest (eq x) 
              where rest q = let y:ys = acc q in (x:y):ys 

我的问题很简单:我知道foldr相似接受3个参数:一个函数,一个初始值和一个列表。但是在代码的第二行中,foldr需要4个参数。 为什么会发生这种情况? 谢谢。

回答

2

Scott的回答是正确的,foldr的结果是一个函数,所以这就是为什么foldr看起来需要4个参数。该foldr功能确实需要3个参数(函数,基地,列表):

*Main> :type foldr 
foldr :: (a -> b -> b) -> b -> [a] -> b 

我就在这里给出的例子是不太复杂:

inc :: Int -> (Int -> Int) 
inc v = \x -> x + v 

test = inc 2 40 -- output of test is 42 

在上面的代码,inc需要一个参数v,并返回一个函数,其参数递增v

我们可以看到下面的inc 2返回类型是一个函数,所以它的参数可以简单地在结尾处添加:

*Main> :type inc 
inc :: Int -> Int -> Int 
*Main> :type inc 2 
inc 2 :: Int -> Int 
*Main> :type inc 2 40               
inc 2 40 :: Int 

括号可以用来强调的是,返回值是一个函数,但在功能上却是相同的,上面的代码:

*Main> (inc 2) 40 
42 

PS:我原来的评论的作者:)

3

在这种情况下,foldr用于构建函数。 (\_ -> False)是该函数的参数。

8

Haskell中的所有函数只有一个参数。当我们有一个a -> b -> c类型的函数时,它只是编写a -> (b -> c)的一个简短方法,即函数,它接受一个参数并产生一个接受另一个参数的函数。有关更多信息,请参阅Currying

在这种情况下,请参阅@ sepp2k的答案。 foldr产生一个函数,它需要另一个(“第四个”)参数。

8

在这种情况下,我认为这是最好看的foldr类型签名:

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

,并匹配到我们有表达(为了清楚起见,加括号):

(foldr step (\_ -> [[]]) xs) (\_ -> False) 

foldr的第二个参数与其结果相同。在这种情况下,第二个参数是一个函数。在这种情况下,这意味着具有3个参数的foldr表达式将是一个函数。

你看到的是foldr函数的第四个参数也可以被认为是foldr结果的第一个参数!