함수형 프로그래밍/python

python curry update

thebirghtwide 2021. 7. 16. 23:59

이전에 작성했던 python curry 문이다. 

def curry(func) :
    curry.__func_name__ = func.__name__ 
    f_args, f_kwargs = [], {}
    def f(*args, **kwargs) :
        nonlocal f_args, f_kwargs 
        if args or kwargs :
            print("args", args, "kwargs", kwargs)
            f_args += args 
            f_kwargs.update(kwargs)
            return f 
        else :
            print( f_args, f_kwargs)
            result = func( *f_args, **f_kwargs)
            f_args, f_kwargs = [], {}
            return result
    return f

def add (a, b) :
    return a + b

add2 = curry(add)
print(add2(1)(2)())

 

 

위 코드를 보면 마지막에 print(add2(1)(2)())  이렇게 작성함으로서 값을 작성한 것을 알 수 있다...

이를 보면 굉장히 번거로워 보인다는 것을 알 수 있다. 함수를 실행 시키기 위해 add2(1)(2) 는 함수 변수만 선언된 상태이다

>>> print(add2(1)(2))
<function curry.<locals>.f at 0x000002C2D9A89B88>

직접 프린트 해보면 다음과 변수 유형 (즉, 함수)과 객체의 메모리 주소 (0x10efa6e50)가 표시된다. 

여기서 추가적으로 함수 값을 호출하려면 () 괄호를 추가로 넣어주어야 

>>> print(add2(1)(2)())
3

다음 과 같이 값이 리턴되는 것을 알 수 있다. 

 

|    문제점 

우리는 좀 더 함수 curry를 사용하고 싶다. add(1)(2)만 써서 말이다. 이를 위해서는 함수 코드를 조금 수정해볼 필요가 있다. 

def curry(func) :
    curry.__func_name__ = func.__name__ 
    f_args, f_kwargs = [], {}
    def f(*args, **kwargs) :
        nonlocal f_args, f_kwargs 
        if args or kwargs :    <------- 조건
            print("args", args, "kwargs", kwargs)
            f_args += args 
            f_kwargs.update(kwargs)
            return f 
        else :
            print( f_args, f_kwargs)
            result = func( *f_args, **f_kwargs)
            f_args, f_kwargs = [], {}
            return result
    return f

curry 함수에서 f함수의 조건을 살펴 보면

if args or kwargs :  일 경우 즉, func 함수 이후 받은 매개변수로 입력받는 변수들이 있을 때 마다

f_args, f_kwargs를 업데이트 혹은 추가 하고 

else : 아니면 func함수에 받았던 저장해 두었던 변수 f_args, f_kwargs을 넣어 return 하겠다는 뜻이다. 

여기서 보이는 문제점은 함수의 호출조건이 args, kwargs가 빈 값일 때 그 때서야 모아두었던 함수의 호출값을 리턴해준다는 것이다. 한 가지의 조건만 추가한다면 func의 변수 갯수 만큼 매개변수가 들어 왔을 때 함수를 호출하여 값을 리턴해 줄 수 있을 것이다.

 

|   문제해결

일단 기존 코드의 조건문을 살펴 보자 

def f(*args, **kwargs) :
        nonlocal f_args, f_kwargs 
        if args or kwargs :
            print("args", args, "kwargs", kwargs)
            f_args += args 
            f_kwargs.update(kwargs)
            return f 
        else :
            print( f_args, f_kwargs)
            result = func( *f_args, **f_kwargs)
            f_args, f_kwargs = [], {}
            return result

현재 단순히 args or kwargs 가 있는지 없는지만 보고 있는 상태에서

하나의 조건을 추가하여 func의 매개변수 길이 만큼 들어 왔을 때 제어 하는 로직을 만들어보자 

 

if args or kwargs :
    print("args", args, "kwargs", kwargs)
    f_args += args 
    f_kwargs.update(kwargs)
    return f

 

args 혹은 kwargs를 신규로 받았을 때 f_args, f_kwargs를 추가하거나 업데이트 하고 바로 함수 f 를 호출하고 있다. 여기서 조건을 추가해보자

if args or kwargs :
    print("args", args, "kwargs", kwargs)
    f_args += args 
    f_kwargs.update(kwargs)
    if len(f_args) + len(f_kwargs) >= func.__code__.co_argcount  : 
        result = func( *f_args, **f_kwargs)
        f_args, f_kwargs = [], {}
        return result
    return f

지금 보면 if len(f_args) + len(f_kwargs) >= func.__code__.co_argcount  :  조건을 추가한 것을 볼 수 있다. 
func.__code__.co_argcount 의 뜻은 함수의 총 매개변수의 갯수를 가져온다는 뜻이다. 예를 들어 

def add (a, b, name ='foo', car = 'in') :
    return a + b

add.__code__.co_argcount
4

위 처럼 사용 될 수 있다. 즉 if len(f_args) + len(f_kwargs) >= func.__code__.co_argcount  : 조건의 뜻은 현재까지 받은 총 매개변수의 합이 함수의 매개변수의 합보다 크거나 같다면 바로 결과를 출력 해주겠다는 뜻이다. 그럼 이렇게 코드를 수정해서 curry문을 만들어 보면 

def curry(func) :
    curry.__func_name__ = func.__name__ 
    f_args, f_kwargs = [], {}
    def f(*args, **kwargs) :
        nonlocal f_args, f_kwargs 
        if args or kwargs :
            print("args", args, "kwargs", kwargs)
            f_args += args 
            f_kwargs.update(kwargs)
            if len(f_args) + len(f_kwargs) >= func.__code__.co_argcount  : 
                result = func( *f_args, **f_kwargs)
                f_args, f_kwargs = [], {}
                return result
            return f 
        else :
            print( f_args, f_kwargs)
            result = func( *f_args, **f_kwargs)
            f_args, f_kwargs = [], {}
            return result
    return f

def add (a, b) :
    return a + b

add2 = curry(add)
print(add2(1)(2))

'''
args (1,) kwargs {}
args (2,) kwargs {}
3
'''

다음과 같이 add2(1)(2)를 바로 사용 할 수 있게 된다. 이제 좀 더 curry 다운 curry가 완성 되었다.