局部函数实现add(1)(2)(3)
点击上方蓝色的“疯狂软件李刚”添加关注
导读本文主要介绍如何通过局部函数(高阶函数)来实现函数curry,国内翻译为函数柯里化(这翻译太操蛋了)。这样可通过一个函数同时实现如下调用:
add(1)(2)(3)
add(1, 2)(3)
add(1)(2, 3)
add(1, 2, 3)
一道“难”题
每天都要在各个读者群内看一看,看看各读者有没有遇到难题,一般读者们会互相帮助、解决问题,如果读者们搞不定,我会顺手把问题解决了。
今天看到读者群内有人在问:
这道题的需求在于,同一个函数可以自动处理:
add(1)(2)(3)
add(1, 2)(3)
add(1)(2, 3)
add(1, 2, 3)
解决思路
乍一看这题的解决思路就是:让函数返回局部函数。
保证add(1)之后再次返回函数,从而保证add(1)(2)可以调用;
而且还要保证add(1)(2)之后还是返回函数,从而保证add(1)(2)(3)可以调用
如何定义嵌套函数?
如何让函数再次返回函数?
这些都是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 wa
print(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 wa
print(add_curry_curry(1)(2)(3))
理一下上面代码定义了几个函数?
add_curry_curry函数包含一个嵌套函数wa,且该函数将wa函数作为返回值。
wa函数包含一个嵌套函数foo,且该函数将foo函数作为返回值。
是不是一切都明朗了?其实并不难,对不对?——说穿了,其实无非就是定义局部函数、返回局部函数。
每当你觉得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) ,如果是,则执行当前函数;如果是小于,则返回一个函数。
# 工具函数:用于对目标函数执行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))
其实就这么简单,所以我在读者群里顺手把这个问题解决了,把这段代码贴给他们。
为了帮助读者能理解我的解决思路,所以顺手写了本文。
关于更多Python编程的深入技巧可参考李刚老师的《疯狂Python讲义》
喜欢请分享到朋友圈
长按二维码轻松关注