我是Haskell的新手,我正在阅读这本书"Real World Haskell"。在本书的第4章中,作者要求使用fold重写groupBy函数。本书的一位读者(Octavian Voicu)给出了以下解决方案:
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个参数。 为什么会这样? 谢谢。
答案 0 :(得分:9)
在这种情况下,我认为最好查看foldr
的类型签名:
foldr :: (a -> b -> b) -> b -> [a] -> b
并将其与我们所拥有的表达式相匹配(为了清楚起见,添加了括号):
(foldr step (\_ -> [[]]) xs) (\_ -> False)
foldr
的第二个参数与其结果的类型相同。在这种情况下,第二个参数是一个函数。在这种情况下,这意味着带有3个参数的foldr
表达式将是一个函数。
你认为折叠函数的第四个参数也可以被认为是foldr结果的第一个参数!
答案 1 :(得分:8)
Haskell中的所有函数只接受一个参数。当我们有一个类型为a -> b -> c
的函数时,它只是一种写a -> (b -> c)
的简短方法,即一个函数,它接受一个参数并产生一个带另一个参数的函数。有关详细信息,请参阅Currying。
在这种情况下,请参阅@ sepp2k的答案。 foldr
生成一个函数,它需要另一个(“第4个”)参数。
答案 2 :(得分:3)
在这种情况下,foldr
用于构建函数。 (\_ -> False)
是该函数的参数。
答案 3 :(得分: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:我是原评论的作者:)