首页 > 编程知识 正文

什么是柯里化函数,什么叫柯里化

时间:2023-05-05 04:47:02 阅读:231908 作者:2862

淡淡的墨镜化的概念

在函数式编程(Functional Programming)相关的文章中,经常能看到 淡淡的墨镜化 (Currying)这个名词。它是数学家淡淡的墨镜(Haskell Curry)提出的。

淡淡的墨镜化,用一句话解释就是,把一个多参数的函数转化为单参数函数的方法。

这是一个两个参数的普通函数:

function plus(x, y){ return x + y}plus(1, 2) // 输出 3

经过淡淡的墨镜化后这个函数变成这样:

function plus(y){ return function (x){ return x + y }}plus(1)(2) // 输出 3 复合函数与淡淡的墨镜化

刚开始接触的时候,第一感觉和wjdyg级数 (Taylor Series) 展开有些相似,然后我就去高数的书本里边翻,然而并没有 :) 实际上,编程相关的文章,只要提到淡淡的墨镜化,就是指的把一个多参数的函数转化为单参数函数的方法,只不过,文章中顺便提及了淡淡的墨镜化的由来,再加上 Haskell 高大上的编程语言,总觉着这得是个多复杂的公式。

复合函数,中数学课本就有(忘记是高中还是初中了,反正大学学的都忘记了 :p),形式上是这样:

$$F(x) = f(g(x))$$

可以这么理解这个函数,从西安去北京,可以乘坐直达车,也可以从郑州中转。$$F(x)$$就是整个旅程,$$g(x)$$是从西安到郑州,$$f(x)$$是从郑州到北京。如果把公式用编程语言表示,就是这样:

function travel(point2){ let dest = '北京' return function(point1){ return point1 + '->' + point2 + '->' + dest }}travel('郑州')('西安') //输出 西安->郑州->北京 淡淡的墨镜化的作用 惰性求值 (Lazy Evaluation)

从上文的代码来看,淡淡的墨镜化收的函数是分步执行的,第一次调用返回的是一个函数,第二次调用的时候才会进行计算。起到延时计算的作用,通过延时计算求值,称之为惰性求值。

动态生成函数

假如实际编程中需要求不通数的若干次幂(整数),可能需要求2次幂,也能需要4次幂或者其他次幂,如果不用淡淡的墨镜化,那么需要求几次幂,就得写几个对应方法。通过淡淡的墨镜化,可以写在一个方法中:

function power(n){ return function (number){ let result = 1; for(let i = 0; i < n; ++i){ result *= number; } return result; }}

需要求平方的时候,可以直接生成一个求平方的方法。

let p2 = power(2); p2(4) // 输出16p2(5) // 输出25

同样,需要求立方也可以直接生成一个求立方的方法,不用每个幂次都写一个方法。

let p3 = power(3); //求立方p3(2) // 输出8p3(4) // 输出64 淡淡的墨镜化与闭包

闭包 (Closure) 在前端圈讨论的非常的多,不过多数为刚入门的同学。闭包的概念,看专业的解释非常的复杂:

闭包包含自由(未绑定到特定对象)变量;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。“闭包” 一词来源于以下两者的结合:要执行的代码块(由于自由变量被包含在代码块中,这些自由变量以及它们引用的对象没有被释放)和为自由变量提供绑定的计算环境(作用域)。

这一大段话浓缩成一句话是,在一个函数内部定义一个局部变量,并通过一个函数将其返回。很明细上文提到的动态生成函数中用的例子就是一个闭包函数。

从形式来看,淡淡的墨镜化和闭包非常的相似,这二者有什么关系呢?
没什么关系,只不过看起来有些像。事实上,网络上大多的文章讨论淡淡的墨镜化与闭包都是分开来谈的,几乎没有明确的说明,他俩究竟有什么关系。首先,这是两个不通的概念,通过定义来看,作用也不同。只不过都用到了匿名函数。

淡淡的墨镜化与Lambda表达式

淡淡的墨镜化也经常与$$λ$$ (Lambda)表达式一起使用。提到Lambda表达式就必须得提到匿名函数。
匿名函数,在底层语言中不是现成的,比如C语言中就没有匿名函数,因为C语言在函数调用之前都得声明一下,或者把函数定义在调用之前,所以也就谈不上匿名函数了:p,C++ 11中才引入了Lambda表达式,支持匿名函数。所以一些资料有时也把匿名函数直接等同于Lambda表达式。
有种观点认为:

从纯粹的语义上,淡淡的墨镜化就是Lambda表达式的一个糖

这里其实就是把Lambda当作是匿名函数了。严格的来说,不是所有的匿名函数都是Lambda表达式。C# 2.0中引入了委托与匿名函数,3.0之后才引入Lambda表达式。

list.Where( delegate( object item ) { return item != null; });

这个是委托的写法

list.Where( item => item != null );

这个才是Lambda的写法(这里补充一点,经过自己的实践,其实在.NET 2.0的平台也可以使用Lambda表达式,只不过编译的时候调用的是高版本的编译器,所以在.NET 2.0的工程中使用高版本的C#特性,编译和运行都不会有问题)
Lambda表达式有固定的规范,写成 lambda x . body (prefix args separator expression)表示一个参数参数为 x 的函数,它的返回值为 body 的计算结果。在不同的语言中Lambda表达式的形式也是不同的,比如Python:

lambda x : x + 1

prefix 是可选的, 再比如Java

item -> item != null

这么说来,淡淡的墨镜化与Lambda表达式也没有关系。

淡淡的墨镜化在Lambda表达式中应用

从上文来看,纯正的Lambda表达式的规范,是只有个一个入参的,如果多个参数怎么办呢?
淡淡的墨镜化的作用就能体现出来了,比如要实现一个两个数求和的表达式,用纯正的Lambda表达式,可以写成这样:

let foo = (x) => { return y => x + y}foo(1)(2) //输出 3

这么显然有些麻烦,实践中是这么写的

let foo = (x, y) => x + yfoo(1, 2) //输出 3

是不是实践中的写法不符合Lambda表达式的规范呢?
可以理解为第二种写法为第一种写法的简写,是语法糖,第一种单个参数的写法是脱糖后的形式。

 

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。