파이썬 카레 및 부분
codewars.com에서 프로그래밍 연습을하는 동안 커링과 부분 함수에 대한 연습을 했습니다.
프로그래밍 초보자이자 주제에 익숙하지 않은 나는 인터넷에서 주제에 대한 정보를 검색하고 연습 문제를 해결하는 데 상당히 깊이 빠져 들었습니다. 그러나 나는 극복 할 수없는 장애물을 우연히 발견했고 여기에서 올바른 방향으로 넛지를 찾고 있습니다.
연습은 다소 간단합니다. 입력 함수를 카레 및 / 또는 부분적으로 분할 할 수있는 함수를 작성하고 충분한 입력 매개 변수가 제공되면 입력 함수를 평가합니다. 입력 함수는 원하는 수의 입력 매개 변수를받을 수 있습니다. 또한 curry / partial 함수는 호출 방법이 매우 유연해야하며 함수를 호출하는 다양한 방법을 처리 할 수 있어야합니다. 또한 curry / partial 함수는 입력 함수에서 요구하는 것보다 더 많은 입력으로 호출 할 수 있습니다.이 경우 모든 초과 입력을 무시해야합니다.
연습 링크를 따라 가면 함수가 처리 할 수 있어야하는 모든 테스트 케이스를 찾을 수 있습니다.
내가 생각 해낸 코드는 다음과 같습니다.
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, 합계 = 6
이것이 실패하는 이유 curry_partial(add, a)
는 함수 핸들을 함수에 반환 하기 때문 g
입니다. 두 번째 호출 인 curry_partial(<function_handle to g>, b)
에서 계산 minArgs = len(signature(func).parameters)
은 내가 원하는대로 작동하지 않습니다. 왜냐하면 이제 함수 g
가 필요한 입력 인수 수 ( 1
즉 :)를 계산 *myArgs
하고 원본에 func
여전히 필요한 수를 계산하기 때문입니다. 따라서 질문은 원본에 func
여전히 필요한 입력 인수 수를 추적 할 수 있도록 코드를 작성하는 방법입니다 (주어진 초기 인수로 함수를 부분 화할 때마다 해당 숫자를 줄임).
아직 프로그래밍과 커링 / 부분에 대해 배울 것이 많기 때문에 가장 편리한 접근 방식을 선택하지 않았을 가능성이 큽니다. 하지만 배우고 싶습니다. 저에게있어이 연습의 어려움은 부분과 카레의 조합입니다. 즉, 발생하는 초기 논쟁을 부분적으로 나누면서 카레 루프를 수행하는 것입니다.
답변
이것을 시도하십시오.
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
하여 플립 플롭과 같은 모든 내부 작업을 수행해야합니다 (플립 플롭이 필요하지 않습니다 ;-)).
임의의 인수를받는 함수가있을 때마다 함수를 전달하는 해당 클래스를 인스턴스화 할 수 있습니다. 그러나 이번에는 당신에게 맡기겠습니다.
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과 같은 많은 순수 기능 언어에는이 기능이 언어에 내장되어 있습니다.