6

我是 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 个参数。为什么会发生这种情况? 谢谢你。

4

4 回答 4

10

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

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

并将其与我们拥有的表达式匹配(为清楚起见添加了括号):

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

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

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

于 2011-01-19T08:20:36.553 回答
8

Haskell 中的所有函数都只接受一个参数。当我们有一个具有 type 的函数时a -> b -> c,它只是一种更短的编写方式a -> (b -> c),即一个函数,它接受一个参数并产生一个接受另一个参数的函数。有关更多信息,请参阅柯里化。

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

于 2011-01-19T08:18:17.100 回答
3

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

于 2011-01-19T08:11:13.893 回答
2

斯科特的回答是正确的,结果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:我是原评论的作者:)

于 2011-01-19T11:31:17.210 回答