Python 柯里化和部分
Python currying and partial
在 codewars.com 上做编程练习时,我遇到了 exercise 柯里化和部分函数。
作为编程新手和该主题的新手,我在 Internet 上搜索了有关该主题的信息,并在解决练习方面取得了很大进展。然而,我现在偶然发现了一个我似乎无法克服的障碍,我在这里寻找正确方向的推动力。
练习相当简单:编写一个函数,可以柯里化 and/or 部分任何输入函数,并在提供足够的输入参数后评估输入函数。输入函数可以接受任意数量的输入参数。此外,curry/partial 函数在调用方式上应该非常灵活,能够处理许多不同的函数调用方式。此外,curry/partial 函数允许使用比输入函数所需更多的输入来调用,在这种情况下,需要忽略所有多余的输入。
经过练习link,可以发现函数需要能够处理的所有测试用例
我想出的代码如下:
from functools import partial
from inspect import signature
def curry_partial(func, *initial_args):
""" Generates a 'curried' version of a function. """
# Process any initial arguments that where given. If the number of arguments that are given exceeds
# minArgs (the number of input arguments that func needs), func is evaluated
minArgs = len(signature(func).parameters)
if initial_args:
if len(initial_args) >= minArgs:
return func(*initial_args[:minArgs])
func = partial(func, *initial_args)
minArgs = len(signature(func).parameters)
# Do the currying
def g(*myArgs):
nonlocal minArgs
# Evaluate function if we have the necessary amount of input arguments
if minArgs is not None and minArgs <= len(myArgs):
return func(*myArgs[:minArgs])
def f(*args):
nonlocal minArgs
newArgs = myArgs + args if args else myArgs
if minArgs is not None and minArgs <= len(newArgs):
return func(*newArgs[:minArgs])
else:
return g(*newArgs)
return f
return g
现在执行以下测试时此代码失败:
test.assert_equals(curry_partial(curry_partial(curry_partial(add, a), b), c), sum)
其中 add = a + b + c(正确定义的函数),a = 1,b = 2,c = 3,sum = 6。
失败的原因是 curry_partial(add, a)
returns 函数 g
的函数句柄。在第二次调用 curry_partial(<function_handle to g>, b)
中,计算 minArgs = len(signature(func).parameters)
没有像我希望的那样工作,因为它现在将计算函数 g
需要多少输入参数(即 1
:即 *myArgs
),而不是原来的 func
仍然需要多少。所以问题是,我如何编写我的代码,以便我可以跟踪我的原始 func
仍然需要多少输入参数(每次我使用任何给定的初始参数对函数进行分部时减少该数字)。
关于编程和 currying/partial,我还有很多东西要学习,所以很可能我没有选择最方便的方法。但我想学习。对我来说,这个练习的难点在于 partial 和 curry 的组合,即在对遇到的任何初始参数进行 partially 的同时执行 curry 循环。
试试这个。
from inspect import signature
# Here `is_set` acts like a flip-flop
is_set = False
params = 0
def curry_partial(func, *partial_args):
"""
Required argument: func
Optional argument: partial_args
Return:
1) Result of the `func` if
`partial_args` contains
required number of items.
2) Function `wrapper` if `partial_args`
contains less than the required
number of items.
"""
global is_set, params
if not is_set:
is_set = True
# if func is already a value
# we should return it
try: params = len(signature(func).parameters)
except: return func
try:
is_set = False
return func(*partial_args[:params])
except:
is_set = True
def wrapper(*extra_args):
"""
Optional argument: extra_args
Return:
1) Result of the `func` if `args`
contains required number of
items.
2) Result of `curry_partial` if
`args` contains less than the
required number of items.
"""
args = (partial_args + extra_args)
try:
is_set = False
return func(*args[:params])
except:
is_set = True
return curry_partial(func, *args)
return wrapper
这确实不是很好的设计。相反,您应该使用 class
来完成所有内部工作,例如 flip-flop(别担心,我们在那里不需要任何 flip-flop ;-))。
只要有一个函数接受任意参数,您总是可以实例化 class 传递该函数。不过这一次,就交给你了。
我不确定 currying,但如果您需要一个简单的部分函数生成器,您可以尝试这样的事情:
from functools import partial
from inspect import signature
def execute_or_partial(f, *args):
max = len(signature(f).parameters)
if len(args) >= max:
return f(*args[:max])
else:
return partial(f, *args)
s = lambda x, y, z: x + y + z
t = execute_or_partial(s, 1)
u = execute_or_partial(t, 2)
v = execute_or_partial(u, 3)
print(v)
or
print(execute_or_partial(execute_or_partial(execute_or_partial(s, 1), 2), 3))
即使不能解决你原来的问题,看看你能不能使用上面的代码来减少代码重复(我不确定,但我认为内部函数中有一些代码重复?);这将使后续问题更容易解决。
标准库中的函数可能已经解决了这个问题。许多纯函数式语言,如 Haskell 都在语言中内置了此功能。
在 codewars.com 上做编程练习时,我遇到了 exercise 柯里化和部分函数。
作为编程新手和该主题的新手,我在 Internet 上搜索了有关该主题的信息,并在解决练习方面取得了很大进展。然而,我现在偶然发现了一个我似乎无法克服的障碍,我在这里寻找正确方向的推动力。
练习相当简单:编写一个函数,可以柯里化 and/or 部分任何输入函数,并在提供足够的输入参数后评估输入函数。输入函数可以接受任意数量的输入参数。此外,curry/partial 函数在调用方式上应该非常灵活,能够处理许多不同的函数调用方式。此外,curry/partial 函数允许使用比输入函数所需更多的输入来调用,在这种情况下,需要忽略所有多余的输入。
经过练习link,可以发现函数需要能够处理的所有测试用例
我想出的代码如下:
from functools import partial
from inspect import signature
def curry_partial(func, *initial_args):
""" Generates a 'curried' version of a function. """
# Process any initial arguments that where given. If the number of arguments that are given exceeds
# minArgs (the number of input arguments that func needs), func is evaluated
minArgs = len(signature(func).parameters)
if initial_args:
if len(initial_args) >= minArgs:
return func(*initial_args[:minArgs])
func = partial(func, *initial_args)
minArgs = len(signature(func).parameters)
# Do the currying
def g(*myArgs):
nonlocal minArgs
# Evaluate function if we have the necessary amount of input arguments
if minArgs is not None and minArgs <= len(myArgs):
return func(*myArgs[:minArgs])
def f(*args):
nonlocal minArgs
newArgs = myArgs + args if args else myArgs
if minArgs is not None and minArgs <= len(newArgs):
return func(*newArgs[:minArgs])
else:
return g(*newArgs)
return f
return g
现在执行以下测试时此代码失败:
test.assert_equals(curry_partial(curry_partial(curry_partial(add, a), b), c), sum)
其中 add = a + b + c(正确定义的函数),a = 1,b = 2,c = 3,sum = 6。
失败的原因是 curry_partial(add, a)
returns 函数 g
的函数句柄。在第二次调用 curry_partial(<function_handle to g>, b)
中,计算 minArgs = len(signature(func).parameters)
没有像我希望的那样工作,因为它现在将计算函数 g
需要多少输入参数(即 1
:即 *myArgs
),而不是原来的 func
仍然需要多少。所以问题是,我如何编写我的代码,以便我可以跟踪我的原始 func
仍然需要多少输入参数(每次我使用任何给定的初始参数对函数进行分部时减少该数字)。
关于编程和 currying/partial,我还有很多东西要学习,所以很可能我没有选择最方便的方法。但我想学习。对我来说,这个练习的难点在于 partial 和 curry 的组合,即在对遇到的任何初始参数进行 partially 的同时执行 curry 循环。
试试这个。
from inspect import signature
# Here `is_set` acts like a flip-flop
is_set = False
params = 0
def curry_partial(func, *partial_args):
"""
Required argument: func
Optional argument: partial_args
Return:
1) Result of the `func` if
`partial_args` contains
required number of items.
2) Function `wrapper` if `partial_args`
contains less than the required
number of items.
"""
global is_set, params
if not is_set:
is_set = True
# if func is already a value
# we should return it
try: params = len(signature(func).parameters)
except: return func
try:
is_set = False
return func(*partial_args[:params])
except:
is_set = True
def wrapper(*extra_args):
"""
Optional argument: extra_args
Return:
1) Result of the `func` if `args`
contains required number of
items.
2) Result of `curry_partial` if
`args` contains less than the
required number of items.
"""
args = (partial_args + extra_args)
try:
is_set = False
return func(*args[:params])
except:
is_set = True
return curry_partial(func, *args)
return wrapper
这确实不是很好的设计。相反,您应该使用 class
来完成所有内部工作,例如 flip-flop(别担心,我们在那里不需要任何 flip-flop ;-))。
只要有一个函数接受任意参数,您总是可以实例化 class 传递该函数。不过这一次,就交给你了。
我不确定 currying,但如果您需要一个简单的部分函数生成器,您可以尝试这样的事情:
from functools import partial
from inspect import signature
def execute_or_partial(f, *args):
max = len(signature(f).parameters)
if len(args) >= max:
return f(*args[:max])
else:
return partial(f, *args)
s = lambda x, y, z: x + y + z
t = execute_or_partial(s, 1)
u = execute_or_partial(t, 2)
v = execute_or_partial(u, 3)
print(v)
or
print(execute_or_partial(execute_or_partial(execute_or_partial(s, 1), 2), 3))
即使不能解决你原来的问题,看看你能不能使用上面的代码来减少代码重复(我不确定,但我认为内部函数中有一些代码重复?);这将使后续问题更容易解决。
标准库中的函数可能已经解决了这个问题。许多纯函数式语言,如 Haskell 都在语言中内置了此功能。