F# 函数式编程之 - 柯里化 currying

即使没有专门学习过函数式的人也有可能听说过传说中的柯里化(currying),这是一个比较 “出圈” 的概念,也是函数式编程的重要特性之一。

我会从最简单的情况开始讲述,刚开始你可能觉得无聊,但随着函数的演化,事情会开始变得有趣。

下面请看一个正常的 F# 函数:

let add x y = x + y
let three = add 1 2

上面这个 add 函数共有两个参数 x 和 y,如果调用函数时给齐参数,它返回计算结果,如上所示,第一行定义了一个函数 add, 第二行喂给它两个参数 1 和 2, 它返回计算值 3

所谓柯里化,就是当调用该函数时我们只给它一个参数,它就返回另一个函数,像这样:

let add1 = add 1
let three = add1 2

如上所示,我们给喂给 add 一个参数(整数 1),它自然无法完成计算,如果是普通的命令式编程,这样做会报错,但在函数式里,则会返回一个新的函数 add1, 函数 add 缺少的那一个参数就是 add1 的参数。

add 的类型是 int -> int -> int (前两个 int 是参数,最后一个 int 是返回值)
add1 的类型是 (int -> int) (是一个函数)

如果给 add1 喂参数 2,此时参数齐了,会返回计算结果 3

另外,还可以这样操作:

let addX x = add x
let add1 = addX 1
let three = add1 2

在上面的代码中, addX 的类型是 int -> (int -> int), 意思是 addX 这个函数接受一个参数 int, 返回值是一个函数 (int -> int)

事情开始变得有趣了,还可以这样操作:

let add x y = x + y
let addX x = add x
let addXY x y = addX x y
let three = addXY 1 2

addXY 的类型(函数签名)是 int -> int -> int (正好与 add 的签名一模一样)

请仔细看,在上面的代码中,第三行(即 addXY 那行)里有这样的内容 addX x, 而在第二行也有相同的内容 addX x, 在上一篇文章(F# 函数式编程之 - 前所未见的 unit 类型)中说过 “函数式编程以数学里的 函数 概念为基础”,那么,也就是说:

已知 addXY x y = addX x y, 同时已知 addX x = add x, 可得 addXY x y = add x y

这正是 addXY 的签名与 add 的签名一模一样的原因,他们的计算结果也是一样的。

故事还没有结束,下面继续演化,是更有趣的事情。

在普通的命令式编程里,加号 + 是一个运算符,但在 F# 里,加号 + 本质上是一个函数!而 x + y 其实是 (+) x y 的语法糖!

也就是说,当我们写下这句 let add x y = x + y, 事实上等于写了 let add x y = (+) x y

仔细观察,不难发现,我们自己定义的函数 add, 与 F# 自带的函数 (+) 是一模一样的。

本文到此结束,从最常见的情形出发,经过简单而曲拆的柯里化变换之后,我们又回到了最初的地方,但经过这一小段旅程,我相信你和我一样,再看同一行代码,会有一种透过表象看到本质的感觉。

posted @ 2020-12-02 09:11  cmdOptionKana  阅读(217)  评论(0)    收藏  举报