前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >局部函数实现add(1)(2)(3)

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

作者头像
疯狂软件李刚
发布2020-07-07 14:57:05
6050
发布2020-07-07 14:57:05
举报
文章被收录于专栏:疯狂软件李刚

导读

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

add(1)(2)(3)

add(1, 2)(3)

add(1)(2, 3)

add(1, 2, 3)

一道“难”题

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

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

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

代码语言:javascript
复制
add(1)(2)(3)  # 6
add(1, 2)(3)  # 6
add(1)(2, 3)  # 6
add(1, 2, 3)  # 6

解决思路

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

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

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

如何定义嵌套函数?

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

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

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

代码语言:javascript
复制
def add (x, y):
    return x + y;
print(add(1, 2))

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

代码语言:javascript
复制
# curry之后的add函数
def add_curry(x):
    def wa(y):
        return x + y
    return wa
print(add_curry(1)(2))

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

代码语言:javascript
复制
def add_curry_curry(x):
    def wa(y):
        def foo (z):
            return x + y + z
        return foo
    return wa
print(add_curry_curry(1)(2)(3))

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

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

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

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

提示

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

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

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

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

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

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

最后解决

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

代码语言:javascript
复制
add(1, 2, 3)
add(1, 2)(3)
add(1)(2, 3)

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

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

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

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

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

代码语言:javascript
复制
# 工具函数:用于对目标函数执行curry
def 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 wa
def test(x, y, z) :
    return x + y + z
# 对test函数执行curry
add = curry(test)
print(add(1)(2)(3))
print(add(1, 2)(3))
print(add(1)(2, 3))
print(add(1, 2, 3))

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

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-07-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 疯狂软件李刚 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档