什么是柯里化?

柯里化的概念

在函数式编程(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

复合函数与柯里化

刚开始接触的时候,第一感觉和泰勒级数 (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) // 输出16
p2(5) // 输出25

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

let p3 = power(3); //求立方
p3(2) // 输出8
p3(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 . bodyprefix 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 + y

foo(1, 2) //输出 3

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

 

  • 23
    点赞
  • 73
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
柯里化是一种函数式编程的概念,它指的是将一个接受多个参数的函数转化为一系列只接受单个参数的函数的过程。在Python中,可以通过使用闭包或者装饰器来实现柯里化。 闭包是指在一个内部函数中引用了外部函数的变量,并且该内部函数可以在外部函数调用结束后继续访问这些变量。通过使用闭包,我们可以实现柯里化。下面是一个使用闭包实现柯里化的示例: ```python def add(x): def inner(y): return x + y return inner add_5 = add(5) print(add_5(3)) # 输出 8 ``` 在上面的例子中,`add` 函数接受一个参数 `x`,并返回一个内部函数 `inner`。内部函数 `inner` 接受参数 `y`,并返回 `x + y` 的结果。通过调用 `add(5)`,我们得到了一个新的函数 `add_5`,它只需要一个参数 `y`,并且会将 `x` 固定为 5。这样,我们就实现了柯里化。 除了使用闭包,还可以使用装饰器来实现柯里化。下面是一个使用装饰器实现柯里化的示例: ```python def curry(func): def inner(*args): if len(args) >= func.__code__.co_argcount: return func(*args) return inner return inner @curry def add(x, y): return x + y add_5 = add(5) print(add_5(3)) # 输出 8 ``` 在上面的例子中,我们定义了一个装饰器 `curry`,它接受一个函数 `func` 作为参数,并返回一个新的函数 `inner`。新的函数 `inner` 接受任意数量的参数 `args`,如果参数个数足够,则调用原始函数 `func` 并返回结果;否则,返回自身。通过在 `add` 函数上应用装饰器 `@curry`,我们实现了柯里化

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值