局部函数实现add(1)(2)(3)

共 2311字,需浏览 5分钟

 ·

2020-07-07 11:18

点击上方蓝色的“疯狂软件李刚391b688a21e802838ccf65d204330d01.webp添加关注

7ab6bd6b9ca21a392b0db67910b5b54f.webp

导读

本文主要介绍如何通过局部函数(高阶函数)来实现函数curry,国内翻译为函数柯里化(这翻译太操蛋了)。这样可通过一个函数同时实现如下调用:

add(1)(2)(3)

add(1, 2)(3)

add(1)(2, 3)

add(1, 2, 3)


一道“难”题





每天都要在各个读者群内看一看,看看各读者有没有遇到难题,一般读者们会互相帮助、解决问题,如果读者们搞不定,我会顺手把问题解决了。


今天看到读者群内有人在问:


69855f1a5adff5d3dcd48922c72e4383.webp

4ed3b8cc07c7dca1e129a3330448d064.webp


这道题的需求在于,同一个函数可以自动处理:


add(1)(2)(3)  # 6add(1, 2)(3)  # 6add(1)(2, 3)  # 6add(1, 2, 3)  # 6


解决思路





乍一看这题的解决思路就是:让函数返回局部函数。


保证add(1)之后再次返回函数,从而保证add(1)(2)可以调用;

而且还要保证add(1)(2)之后还是返回函数,从而保证add(1)(2)(3)可以调用


b1508d23d124114bdf54a4d78cfa5a8c.webp


如何定义嵌套函数?

如何让函数再次返回函数?


这些都是Python函数式编程的基础内容,详细讲解可参看《疯狂Python讲义》的5.3和5.4两节。


为了引导大家思维,下面先来点简单的,例如将如下接受两个参数函数转成只接受一个参数(curry)


def add (x, y):    return x + y;print(add(1, 2))


为了对上面add()函数进行curry,可以考虑定义一个嵌套函数,嵌套函数接受add()函数传入的参数,并添加自己的参数,例如改为如下形式:


# curry之后的add函数def add_curry(x):    def wa(y):        return x + y    return waprint(add_curry(1)(2))


按照此思路写一个add(1)(2)(3)吧,


def add_curry_curry(x):    def wa(y):        def foo (z):            return x + y + z        return foo    return waprint(add_curry_curry(1)(2)(3))


理一下上面代码定义了几个函数?


add_curry_curry函数包含一个嵌套函数wa,且该函数将wa函数作为返回值。


wa函数包含一个嵌套函数foo,且该函数将foo函数作为返回值。


是不是一切都明朗了?其实并不难,对不对?——说穿了,其实无非就是定义局部函数、返回局部函数。

3473b7db6b5e036510b97e27fc151091.webp



提示

每当你觉得xxx很难时,往往还是基础不扎实,很多人学编程时难免犯一个方法错误,他把编程知识分成两类:

A:看一眼似乎能学会的。

B:看一眼似乎不能学会的。

对于A类知识,他的反应是:这TM太简单,没必要学习;对于B类知识,他的反应是:这TM太难了,完全超出了我的学习范畴,没法学习。

——所以天底下就没有值得学习的东西。


我的经验是:看似一眼就能学会的知识,你更应该反复练习,找到它们内置的运行机制,以便“温故而知新”;看似学不会的知识,只要多回顾已掌握的知识,就不难发现:“太阳底下没有新知识”——软件领域的创新能力其实非常有限,大部分知识都是模仿、微改变。


最后解决





可能有人会说 ,但我们的要求是一个add函数同时支持下面这几种的用法呢:


add(1, 2, 3)add(1, 2)(3)add(1)(2, 3)


如果你理解了上面两个例子的运行机制(嵌套函数和函数返回值),我们完全可以定义一个工具函数来实现对目标函数的curry。


方法就是,程序只要判断传给函数的参数个数(len(args)与原函数所需参数个数(通过__code__.co_argcount属性获取)的关系即可。


如果传给函数的参数个数大于等于原函数所需的参数个数,则执行函数;否则就返回一个嵌套函数。


我们当然可以自己实现一个工具函数专门来生成 柯里化 函数。

主要思路是什么呢,要判断当前传入函数的参数个数 (args.length) 是否大于等于原函数所需参数个数 (fn.length) ,如果是,则执行当前函数;如果是小于,则返回一个函数。


# 工具函数:用于对目标函数执行currydef curry (fn, *args):    # 如果传入参数大于或等于fn函数所需的参数    if len(args) >= fn.__code__.co_argcount :        # 直接执行fn函数        return fn(*args)    else :        # 定义局部函数        def wa (*b):            # 将args所有函数和*b所有参数作为参数一起传入            return curry(fn, *args, *b)        return wadef test(x, y, z) :    return x + y + z# 对test函数执行curryadd = curry(test)print(add(1)(2)(3))print(add(1, 2)(3))print(add(1)(2, 3))print(add(123))


其实就这么简单,所以我在读者群里顺手把这个问题解决了,把这段代码贴给他们。


为了帮助读者能理解我的解决思路,所以顺手写了本文。


5c884d9130dcf02628406b556822930a.webp



关于更多Python编程的深入技巧可参考李刚老师的《疯狂Python讲义》

fd67c0e2607828226fa4c155fb9ec14f.webp

喜欢请分享到朋友圈

长按二维码轻松关注

浏览 10
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报