是的,您对不需要所有参数的函数的直觉是正确的。当一个函数将另一个函数作为参数返回另一个函数作为结果时,它被称为“currying”。请参阅:http ://en.wikipedia.org/wiki/Currying 。
(作为旁注,这实际上是由Haskell Curry发现(或重新发现)的,这就是我们的 Haskell 得名的原因。)
如果柯里化的想法仍然需要时间来理解,这可能会有所帮助:实际上在Prelude
被调用的curry
和中定义了两个函数uncurry
。它们具有以下类型:
Prelude> :t curry
curry :: ((a, b) -> c) -> a -> b -> c
Prelude> :t uncurry
uncurry :: (a -> b -> c) -> (a, b) -> c
uncurry
接受一个 2 参数的curried函数(或一个接受一个参数的函数并返回一个参数的函数的函数)并生成一个uncurrried函数,或者一个同时接受所有参数的函数(作为一个元组)。
curry
,正如您可能通过名称和类型所暗示的那样,采用另一种方式,因此它接受一个柯里化函数(一个同时接受所有参数的函数)并生成一个接受一个参数并返回一个函数的函数另一个论点。
大多数编程语言默认以非柯里化方式工作,您提供所有参数并获得结果,而 Haskell 默认情况下是柯里化的。
现在以 为例uncurry
,我们可以采用一个简单的函数,(+)
:
Prelude> :t (+)
(+) :: Num a => a -> a -> a
Prelude> (+) 1 2
3
Prelude> :t (+)
(+) :: Num a => a -> a -> a
Prelude> :t uncurry (+)
uncurry (+) :: Num c => (c, c) -> c
Prelude> uncurry (+) (1,2)
3
const
而且,如果我们愿意,我们也可以这样做:
Prelude> :t const
const :: a -> b -> a
Prelude> const 1 2
1
Prelude> :t uncurry const
uncurry const :: (c, b) -> c
Prelude> uncurry const (1,2)
1
但是有一个非柯里化的版本const
并不是很有用,因为如果你必须预先指定所有参数,那么让一个函数接受两个参数并总是返回第一个参数是没有意义的。
const
之所以有用,正是因为它是柯里化的,并且可以在需要一个函数的地方给出,该函数接受两个参数并简单地返回第一个参数。
比如在 中foldr1
:
Prelude> :t foldr1
foldr1 :: (a -> a -> a) -> [a] -> a
其中第一个参数是两个参数的柯里化函数。并且由于函数的返回值可以是一个函数,它也可以是这样的const
:
Prelude> :t const id
const id :: b -> a -> a
const id
只需接受任何类型的参数b
并返回id
函数。
所以如果我们可以const id
一步一步申请:
Prelude> :t const id 1
const id 1 :: a -> a
const id 1
或者const id anyOtherValueHere
只返回 id 函数。可以这样使用:
Prelude> :t const id "Giraffe" 100
const id "Giraffe" 100 :: Num a => a
Prelude> const id "Giraffe" 100
100
Prelude> :t const id (\a b -> undefined) 100
const id (\a b -> undefined) 100 :: Num a => a
Prelude> const id (\a b -> undefined) 100
100
所以,const
真的忽略了它的第二个论点。直接在上面,就像申请id
100一样。
因此,foldr1 (const id)
只需获取一个列表并继续应用于id
每组两个元素并保留第二个(因为const id
返回id
传入的第二个参数的值),直到我们拥有最后一个元素。