Haskell-柯里函数

Haskell - 柯里函数

柯里化(Currying),又称部分求值(Partial Evaluation),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。核心思想是把多参数传入的函数拆成单参数(或部分)函数,内部再返回调用下一个单参数(或部分)函数,依次处理剩余的参数。
比如:

1
2
const auto result = get_sum(1,2,3,4,5); //传统写法
const auto result2 = get_sum(1)(2)(3)(4)(5); //柯里化

在Haskell中所有的函数其实都只有一个参数,我们事实上编辑的所有多参数的函数都是柯里函数。柯里函数不会一次性读完所有参数,而是在每次调用时只取一个参数,并返回一个一元函数来取下一个函数。
所以说在Haskell中这两种调用方法是一样的:

1
2
max 4 5
(max 4) 5

这是怎么实现的呢?先看一下函数原型是啥样子的

1
2
ghci> :t max
max :: Ord a => a -> a -> a

上面的申明也可以写成下面的样子:max :: Ord a => a -> (a -> a)
只要在类型签名中看到 ->,就一律意味着它是一个将箭头左侧是为参数类型并将箭头右侧的部分视作返回类型的函数。如果遇到a -> (a -> a)这样的类型签名,就是说它是一个函数,会接受一个a作为参数,然后返回一个函数,然后这个函数也是取a作为参数,返回一个类型为a的值。

这有什么好处呢?只要用部分参数来调用某个函数,就可以得到一个部分应用函数,此函数接受的参数的数量和之前少传入的参数数量一致。因此可以通过初始的一个简单函数来构造出更复杂的函数。

1
2
multThree :: Int -> Int -> Int -> Int
multThree x y z = x * y * z

使用((multThree 3) 5 ) 9 的方式调用他,一开始接受一个参数3, 那么返回一个函数的一元函数,之后再给一个5, 就会得到3 * 5 的函数, 然后再传一个9, 最后返回135。

其实可以理解将一部分参数丢给一个函数,函数会返回一个带着他一部分结果的小函数,然后这个小函数再接受一个参数,然后再返回一个函数。最后直到函数被灌满结束。
通过这个,我们可以玩一个好玩的

1
2
3
4
5
6
multTwoWithNine :: Int -> Int -> Int
multTwoWithNine = multThree 9
-- 等价于
-- multTwoWith x y = multTHree 9 x y

multTwoWithNine 2 3 -- 返回54

截断(section)

1
2
divideByTen :: (Floating a) => a -> a
divideByTen = (/10)

用括号将一个中缀函数包裹起来,得到一个一元函数,其参数代表函数的剩余参数。这个/函数本来需要左右都有一个参数才可以使用,然后用括号包裹他之后就宛如截断了一样,然后/本来需要两个参数,现在填充了一个参数,divideByTen再提供一个参数。
这样的写法divideByTen 200 和 直接调用 (/10) 200 或者 200 / 10的效果是一样的。

打印函数

如果直接将函数打印出来会怎么样。

1
2
3
4
5
6
7
ghci> multThree 3 4

<interactive>:4:1: error:
• No instance for (Show (Integer -> Integer))
arising from a use of ‘print
(maybe you haven't applied a function to enough arguments?)
• In a stmt of an interactive GHCi command: print it

GHCI: 表达式返回一个类型为 a -> a 函数,但是不知道如何将他显示到屏幕上。函数g并非Show类型类的实例。
为什么我们执行 1+1的时候可以得到2呢?因为它会首先计算得到2, 然后调用show 得到该数字的字符串表示,然后再输出到屏幕上。

所以一定要理解柯里函数的工作原理,在Haskell中真的非常重要!


Haskell-柯里函数
http://cvrain.cloudvl.cn/2023/11/21/Haskell/haskell-currying/
作者
ClaudeRainer
发布于
2023年11月21日
许可协议